Skip to main content

ghostscope_dwarf/parser/
detailed_parser.rs

1//! Detailed DWARF parser for on-demand traversal and variable resolution
2//!
3//! This module handles detailed parsing of DWARF tree structures, including:
4//! - Tree traversal for variable collection
5//! - Variable and parameter DIE parsing
6//! - Scope-aware variable resolution
7
8use crate::{
9    binary::DwarfReader,
10    core::{attr_u64, EvaluationResult, Result},
11    dwarf_expr::{errors as expr_errors, modes::DwarfExprMode},
12    index::{CfiIndex, FunctionBlocks},
13    parser::ExpressionEvaluator,
14    semantics::{resolve_name_with_origins, resolve_type_ref_in_same_unit_with_origins},
15    TypeInfo,
16};
17use gimli::Reader;
18// Alias gimli constants to upper-case identifiers to satisfy naming lints without allow attributes
19use gimli::constants::{
20    DW_AT_byte_size as DW_AT_BYTE_SIZE, DW_AT_encoding as DW_AT_ENCODING, DW_AT_name as DW_AT_NAME,
21    DW_AT_type as DW_AT_TYPE, DW_TAG_array_type as DW_TAG_ARRAY_TYPE,
22    DW_TAG_base_type as DW_TAG_BASE_TYPE, DW_TAG_class_type as DW_TAG_CLASS_TYPE,
23    DW_TAG_const_type as DW_TAG_CONST_TYPE, DW_TAG_enumeration_type as DW_TAG_ENUMERATION_TYPE,
24    DW_TAG_pointer_type as DW_TAG_POINTER_TYPE, DW_TAG_restrict_type as DW_TAG_RESTRICT_TYPE,
25    DW_TAG_structure_type as DW_TAG_STRUCTURE_TYPE,
26    DW_TAG_subroutine_type as DW_TAG_SUBROUTINE_TYPE, DW_TAG_typedef as DW_TAG_TYPEDEF,
27    DW_TAG_union_type as DW_TAG_UNION_TYPE, DW_TAG_volatile_type as DW_TAG_VOLATILE_TYPE,
28};
29use std::collections::HashSet;
30// no tracing imports needed here
31
32/// Variable with complete information including EvaluationResult
33#[derive(Debug, Clone)]
34pub struct VariableWithEvaluation {
35    pub name: String,
36    pub type_name: String,
37    pub dwarf_type: Option<TypeInfo>,
38    pub evaluation_result: EvaluationResult,
39    pub scope_depth: usize,
40    pub is_parameter: bool,
41    pub is_artificial: bool,
42}
43
44// Removed full traversal request/context types in shallow mode
45
46/// Detailed DWARF parser for tree traversal and variable collection
47#[derive(Debug)]
48pub struct DetailedParser {}
49
50impl DetailedParser {
51    /// Create new detailed parser
52    pub fn new() -> Self {
53        Self {}
54    }
55
56    /// Attach a cross-CU type name index for faster completion
57    pub fn set_type_name_index(&mut self, _index: std::sync::Arc<crate::index::TypeNameIndex>) {}
58
59    // Full type resolution intentionally removed; only shallow type resolution is supported.
60
61    /// Shallow type resolution (no recursive member expansion)
62    /// Returns minimal TypeInfo with name/size where possible.
63    pub fn resolve_type_shallow_at_offset(
64        dwarf: &gimli::Dwarf<DwarfReader>,
65        unit: &gimli::Unit<DwarfReader>,
66        mut type_offset: gimli::UnitOffset,
67    ) -> Option<TypeInfo> {
68        let mut visited = std::collections::HashSet::new();
69        // Strip typedef/qualifiers chain but keep last typedef name if it's the canonical alias
70        let mut alias_name: Option<String> = None;
71
72        let mut step = 0usize;
73        const MAX_STEPS: usize = 64;
74        loop {
75            if step >= MAX_STEPS || !visited.insert(type_offset) {
76                return Some(TypeInfo::UnknownType {
77                    name: "<depth_limit>".to_string(),
78                });
79            }
80            step += 1;
81            let entry = unit.entry(type_offset).ok()?;
82            let tag = entry.tag();
83            // Utility to read attr string name
84            let mut entry_name: Option<String> = None;
85            if let Some(a) = entry.attr(DW_AT_NAME) {
86                if let Ok(s) = dwarf.attr_string(unit, a.value()) {
87                    if let Ok(s_str) = s.to_string_lossy() {
88                        entry_name = Some(s_str.into_owned());
89                    }
90                }
91            }
92            match tag {
93                DW_TAG_TYPEDEF => {
94                    if alias_name.is_none() {
95                        alias_name = entry_name.clone();
96                    }
97                    if let Some(off) =
98                        resolve_type_ref_in_same_unit_with_origins(dwarf, &entry, unit).ok()?
99                    {
100                        type_offset = off;
101                        continue;
102                    }
103                    return Some(TypeInfo::TypedefType {
104                        name: alias_name.unwrap_or_else(|| {
105                            entry_name.unwrap_or_else(|| "<anon_typedef>".to_string())
106                        }),
107                        underlying_type: Box::new(TypeInfo::UnknownType {
108                            name: "<unknown>".to_string(),
109                        }),
110                    });
111                }
112                DW_TAG_CONST_TYPE | DW_TAG_VOLATILE_TYPE | DW_TAG_RESTRICT_TYPE => {
113                    if let Some(off) =
114                        resolve_type_ref_in_same_unit_with_origins(dwarf, &entry, unit).ok()?
115                    {
116                        type_offset = off;
117                        continue;
118                    }
119                    return Some(TypeInfo::QualifiedType {
120                        qualifier: crate::TypeQualifier::Const,
121                        underlying_type: Box::new(TypeInfo::UnknownType {
122                            name: "<unknown>".to_string(),
123                        }),
124                    });
125                }
126                DW_TAG_POINTER_TYPE => {
127                    let mut byte_size: u64 = 8;
128                    // Default to unknown pointee until we find a concrete base/aggregate
129                    let mut target: TypeInfo = TypeInfo::UnknownType {
130                        name: "void".to_string(),
131                    };
132                    if let Some(a) = entry.attr(DW_AT_BYTE_SIZE) {
133                        if let gimli::AttributeValue::Udata(sz) = a.value() {
134                            byte_size = sz;
135                        }
136                    }
137                    // Very shallow pointee name resolution: unwrap typedef/qualifiers only, without recursion
138                    let mut pointee_name: Option<String> = None;
139                    if let Some(mut toff) =
140                        resolve_type_ref_in_same_unit_with_origins(dwarf, &entry, unit).ok()?
141                    {
142                        // Unwrap up to a small bound to avoid deep recursion/stack blowups on real-world code
143                        for _ in 0..8 {
144                            if let Ok(tentry) = unit.entry(toff) {
145                                match tentry.tag() {
146                                    DW_TAG_TYPEDEF | DW_TAG_CONST_TYPE | DW_TAG_VOLATILE_TYPE
147                                    | DW_TAG_RESTRICT_TYPE => {
148                                        // Capture typedef name as a fallback pointee name
149                                        if tentry.tag() == DW_TAG_TYPEDEF {
150                                            if let Some(na) = tentry.attr(DW_AT_NAME) {
151                                                if let Ok(s) = dwarf.attr_string(unit, na.value()) {
152                                                    if pointee_name.is_none() {
153                                                        if let Ok(s_str) = s.to_string_lossy() {
154                                                            pointee_name = Some(s_str.into_owned());
155                                                        }
156                                                    }
157                                                }
158                                            }
159                                        }
160                                        if let Some(next) =
161                                            resolve_type_ref_in_same_unit_with_origins(
162                                                dwarf, &tentry, unit,
163                                            )
164                                            .ok()?
165                                        {
166                                            toff = next;
167                                            continue;
168                                        }
169                                        // No further type; bail
170                                    }
171                                    DW_TAG_BASE_TYPE => {
172                                        // Construct BaseType with size+encoding
173                                        let mut byte_size = 0u64;
174                                        let mut encoding = gimli::constants::DW_ATE_unsigned;
175                                        if let Some(a) = tentry.attr(DW_AT_BYTE_SIZE) {
176                                            if let gimli::AttributeValue::Udata(sz) = a.value() {
177                                                byte_size = sz;
178                                            }
179                                        }
180                                        if let Some(a) = tentry.attr(DW_AT_ENCODING) {
181                                            if let gimli::AttributeValue::Encoding(enc) = a.value()
182                                            {
183                                                encoding = enc;
184                                            }
185                                        }
186                                        let name = if let Some(na) = tentry.attr(DW_AT_NAME) {
187                                            if let Ok(s) = dwarf.attr_string(unit, na.value()) {
188                                                s.to_string_lossy()
189                                                    .ok()
190                                                    .map(|c| c.into_owned())
191                                                    .unwrap_or_else(|| "<base>".into())
192                                            } else {
193                                                "<base>".into()
194                                            }
195                                        } else {
196                                            "<base>".into()
197                                        };
198                                        pointee_name = Some(name.clone());
199                                        target = TypeInfo::BaseType {
200                                            name,
201                                            size: byte_size,
202                                            encoding: encoding.0 as u16,
203                                        };
204                                    }
205                                    DW_TAG_STRUCTURE_TYPE
206                                    | DW_TAG_CLASS_TYPE
207                                    | DW_TAG_UNION_TYPE
208                                    | DW_TAG_ENUMERATION_TYPE => {
209                                        // Do NOT recursively resolve aggregates here to avoid cycles on self-referential types.
210                                        // Only record the name; deref-time upgrade will use analyzer's shallow index safely.
211                                        if let Some(na) = tentry.attr(DW_AT_NAME) {
212                                            if let Ok(s) = dwarf.attr_string(unit, na.value()) {
213                                                if let Ok(s_str) = s.to_string_lossy() {
214                                                    let n = s_str.into_owned();
215                                                    pointee_name = Some(n.clone());
216                                                    // keep target as UnknownType{name} to avoid deep recursion here
217                                                    if matches!(
218                                                        target,
219                                                        TypeInfo::UnknownType { .. }
220                                                    ) {
221                                                        target = TypeInfo::UnknownType { name: n };
222                                                    }
223                                                }
224                                            }
225                                        }
226                                    }
227                                    _ => {}
228                                }
229                                break;
230                            } else {
231                                break;
232                            }
233                        }
234                    }
235                    // If only a name was found without concrete target, carry it as UnknownType
236                    if let Some(n) = pointee_name {
237                        if matches!(target, TypeInfo::UnknownType { .. }) {
238                            target = TypeInfo::UnknownType { name: n };
239                        }
240                    }
241                    return Some(TypeInfo::PointerType {
242                        target_type: Box::new(target),
243                        size: byte_size,
244                    });
245                }
246                DW_TAG_BASE_TYPE => {
247                    let name = entry_name.unwrap_or_else(|| "<base>".to_string());
248                    let mut byte_size = 0u64;
249                    let mut encoding = gimli::constants::DW_ATE_unsigned;
250                    for a in entry.attrs() {
251                        match a.name() {
252                            DW_AT_BYTE_SIZE => {
253                                if let gimli::AttributeValue::Udata(sz) = a.value() {
254                                    byte_size = sz;
255                                }
256                            }
257                            DW_AT_ENCODING => {
258                                if let gimli::AttributeValue::Encoding(enc) = a.value() {
259                                    encoding = enc;
260                                }
261                            }
262                            _ => {}
263                        }
264                    }
265                    return Some(TypeInfo::BaseType {
266                        name,
267                        size: byte_size,
268                        encoding: encoding.0 as u16,
269                    });
270                }
271                DW_TAG_STRUCTURE_TYPE | DW_TAG_CLASS_TYPE => {
272                    let name = alias_name.clone().unwrap_or_else(|| {
273                        entry_name.unwrap_or_else(|| "<anon_struct>".to_string())
274                    });
275                    let mut byte_size = 0u64;
276                    if let Some(a) = entry.attr(DW_AT_BYTE_SIZE) {
277                        if let gimli::AttributeValue::Udata(sz) = a.value() {
278                            byte_size = sz;
279                        }
280                    }
281                    // Collect only direct member DIEs
282                    let mut members: Vec<crate::StructMember> = Vec::new();
283                    if let Ok(mut tree) = unit.entries_tree(Some(entry.offset())) {
284                        if let Ok(root) = tree.root() {
285                            let mut children = root.children();
286                            while let Ok(Some(child)) = children.next() {
287                                let ce = child.entry();
288                                if ce.tag() == gimli::DW_TAG_member {
289                                    // member name
290                                    let mut m_name = String::new();
291                                    if let Some(na) = ce.attr(DW_AT_NAME) {
292                                        if let Ok(s) = dwarf.attr_string(unit, na.value()) {
293                                            if let Ok(s_str) = s.to_string_lossy() {
294                                                m_name = s_str.into_owned();
295                                            }
296                                        }
297                                    }
298                                    // member type (shallow)
299                                    let mut m_type = TypeInfo::UnknownType {
300                                        name: "unknown".to_string(),
301                                    };
302                                    if let Some(gimli::AttributeValue::UnitRef(toff)) =
303                                        ce.attr_value(DW_AT_TYPE)
304                                    {
305                                        if let Some(ti) =
306                                            Self::resolve_type_shallow_at_offset(dwarf, unit, toff)
307                                        {
308                                            m_type = ti;
309                                        }
310                                    }
311                                    // member offset (simple evaluation)
312                                    let mut m_offset: u64 = 0;
313                                    if let Some(ml) = ce.attr(gimli::DW_AT_data_member_location) {
314                                        match ml.value() {
315                                            gimli::AttributeValue::Exprloc(expr) => {
316                                                if let Some(v) = expr_errors::downgrade_optional_to_none(
317                                                    DwarfExprMode::ConstOffset,
318                                                    crate::dwarf_expr::const_eval::eval_const_offset(
319                                                        &expr,
320                                                        unit.encoding(),
321                                                    ),
322                                                    "shallow member type display",
323                                                ) {
324                                                    m_offset = v;
325                                                }
326                                            }
327                                            value => {
328                                                if let Some(v) = attr_u64(value) {
329                                                    m_offset = v;
330                                                }
331                                            }
332                                        }
333                                    }
334                                    // bit offsets/sizes (optional)
335                                    let mut bit_offset: Option<u8> = None;
336                                    let mut raw_bit_offset: Option<u64> = None;
337                                    let mut has_data_bit_offset = false;
338                                    let mut bit_size: Option<u8> = None;
339                                    if let Some(bo) = ce.attr(gimli::DW_AT_bit_offset) {
340                                        if let Some(v) = attr_u64(bo.value()) {
341                                            raw_bit_offset = Some(v);
342                                        }
343                                    }
344                                    if let Some(bs) = ce.attr(gimli::DW_AT_data_bit_offset) {
345                                        if let Some(v) = attr_u64(bs.value()) {
346                                            has_data_bit_offset = true;
347                                            bit_offset = u8::try_from(v % 8).ok();
348                                            m_offset = v / 8;
349                                        }
350                                    }
351                                    if let Some(bsz) = ce.attr(gimli::DW_AT_bit_size) {
352                                        if let Some(v) = attr_u64(bsz.value()) {
353                                            bit_size = u8::try_from(v).ok();
354                                        }
355                                    }
356                                    // DW_AT_bit_offset (legacy) is typically counted from the MSB
357                                    // of the storage unit; convert to little-endian LSB offset.
358                                    // When DW_AT_data_bit_offset is present we already have LSB-based
359                                    // layout and should not convert again.
360                                    if !has_data_bit_offset {
361                                        if let (Some(raw_bo), Some(bs)) = (raw_bit_offset, bit_size)
362                                        {
363                                            let storage_bits = m_type.size().saturating_mul(8);
364                                            let bs_u64 = bs as u64;
365                                            if storage_bits > 0 && raw_bo + bs_u64 <= storage_bits {
366                                                let le_off = storage_bits - raw_bo - bs_u64;
367                                                bit_offset = u8::try_from(le_off).ok();
368                                            } else {
369                                                bit_offset = u8::try_from(raw_bo).ok();
370                                            }
371                                        } else if let Some(raw_bo) = raw_bit_offset {
372                                            bit_offset = u8::try_from(raw_bo).ok();
373                                        }
374                                    }
375                                    if m_name.is_empty() {
376                                        m_name = format!("member_{}", members.len());
377                                    }
378                                    // Wrap bitfield member type into BitfieldType for standalone printing
379                                    let member_type = if let Some(bs) = bit_size {
380                                        let bo = bit_offset.unwrap_or(0);
381                                        TypeInfo::BitfieldType {
382                                            underlying_type: Box::new(m_type),
383                                            bit_offset: bo,
384                                            bit_size: bs,
385                                        }
386                                    } else {
387                                        m_type
388                                    };
389                                    members.push(crate::StructMember {
390                                        name: m_name,
391                                        member_type,
392                                        offset: m_offset,
393                                        bit_offset,
394                                        bit_size,
395                                    });
396                                }
397                            }
398                        }
399                    }
400                    // Post-process: infer array total_size/element_count when missing (from next member offset or struct size)
401                    if !members.is_empty() {
402                        // Pre-build sorted offsets
403                        let mut offsets: Vec<u64> = members.iter().map(|m| m.offset).collect();
404                        offsets.sort_unstable();
405                        offsets.dedup();
406
407                        for m in &mut members {
408                            // Only for array members with missing size info
409                            if let TypeInfo::ArrayType {
410                                element_type,
411                                element_count,
412                                total_size,
413                            } = &m.member_type
414                            {
415                                if element_count.is_none() && total_size.is_none() {
416                                    let cur_off = m.offset;
417                                    let next_off = offsets
418                                        .iter()
419                                        .cloned()
420                                        .filter(|&o| o > cur_off)
421                                        .min()
422                                        .unwrap_or(byte_size);
423                                    let avail = next_off.saturating_sub(cur_off);
424                                    if avail > 0 {
425                                        let elem_sz = element_type.size();
426                                        let mut new_count: Option<u64> = None;
427                                        if elem_sz > 0 && avail % elem_sz == 0 {
428                                            new_count = Some(avail / elem_sz);
429                                        }
430                                        m.member_type = TypeInfo::ArrayType {
431                                            element_type: element_type.clone(),
432                                            element_count: new_count,
433                                            total_size: Some(avail),
434                                        };
435                                    }
436                                }
437                            }
438                        }
439                    }
440                    return Some(TypeInfo::StructType {
441                        name,
442                        size: byte_size,
443                        members,
444                    });
445                }
446                DW_TAG_UNION_TYPE => {
447                    let name = alias_name.clone().unwrap_or_else(|| {
448                        entry_name.unwrap_or_else(|| "<anon_union>".to_string())
449                    });
450                    let mut byte_size = 0u64;
451                    if let Some(a) = entry.attr(DW_AT_BYTE_SIZE) {
452                        if let gimli::AttributeValue::Udata(sz) = a.value() {
453                            byte_size = sz;
454                        }
455                    }
456                    let mut members: Vec<crate::StructMember> = Vec::new();
457                    if let Ok(mut tree) = unit.entries_tree(Some(entry.offset())) {
458                        if let Ok(root) = tree.root() {
459                            let mut children = root.children();
460                            while let Ok(Some(child)) = children.next() {
461                                let ce = child.entry();
462                                if ce.tag() == gimli::DW_TAG_member {
463                                    let mut m_name = String::new();
464                                    if let Some(na) = ce.attr(gimli::DW_AT_name) {
465                                        if let Ok(s) = dwarf.attr_string(unit, na.value()) {
466                                            if let Ok(s_str) = s.to_string_lossy() {
467                                                m_name = s_str.into_owned();
468                                            }
469                                        }
470                                    }
471                                    let mut m_type = TypeInfo::UnknownType {
472                                        name: "unknown".to_string(),
473                                    };
474                                    if let Some(gimli::AttributeValue::UnitRef(toff)) =
475                                        ce.attr_value(DW_AT_TYPE)
476                                    {
477                                        if let Some(ti) =
478                                            Self::resolve_type_shallow_at_offset(dwarf, unit, toff)
479                                        {
480                                            m_type = ti;
481                                        }
482                                    }
483                                    if m_name.is_empty() {
484                                        m_name = format!("member_{}", members.len());
485                                    }
486                                    members.push(crate::StructMember {
487                                        name: m_name,
488                                        member_type: m_type,
489                                        offset: 0,
490                                        bit_offset: None,
491                                        bit_size: None,
492                                    });
493                                }
494                            }
495                        }
496                    }
497                    return Some(TypeInfo::UnionType {
498                        name,
499                        size: byte_size,
500                        members,
501                    });
502                }
503                DW_TAG_ENUMERATION_TYPE => {
504                    let name = alias_name
505                        .clone()
506                        .unwrap_or_else(|| entry_name.unwrap_or_else(|| "<anon_enum>".to_string()));
507                    // Parse base type and size
508                    let mut byte_size = 0u64;
509                    if let Some(a) = entry.attr(DW_AT_BYTE_SIZE) {
510                        if let gimli::AttributeValue::Udata(sz) = a.value() {
511                            byte_size = sz;
512                        }
513                    }
514                    // Default base type as signed int; size from byte_size or 4
515                    let mut base_type: TypeInfo = TypeInfo::BaseType {
516                        name: "int".to_string(),
517                        size: if byte_size > 0 { byte_size } else { 4 },
518                        encoding: gimli::constants::DW_ATE_signed.0 as u16,
519                    };
520                    // If DW_AT_type refers to a base type, resolve it shallowly
521                    if let Some(gimli::AttributeValue::UnitRef(toff)) = entry.attr_value(DW_AT_TYPE)
522                    {
523                        if let Some(ti) = Self::resolve_type_shallow_at_offset(dwarf, unit, toff) {
524                            // Accept only base/qualified/typedef chain base type as enum underlying type
525                            base_type = ti;
526                            // If enum size missing, use underlying base type size
527                            let bs = base_type.size();
528                            if byte_size == 0 && bs > 0 {
529                                byte_size = bs;
530                            }
531                        }
532                    }
533                    // Collect enum variants (one level)
534                    let mut variants: Vec<crate::EnumVariant> = Vec::new();
535                    if let Ok(mut tree) = unit.entries_tree(Some(entry.offset())) {
536                        if let Ok(root) = tree.root() {
537                            let mut children = root.children();
538                            while let Ok(Some(child)) = children.next() {
539                                let ce = child.entry();
540                                if ce.tag() == gimli::DW_TAG_enumerator {
541                                    let mut v_name = String::new();
542                                    if let Some(na) = ce.attr(gimli::DW_AT_name) {
543                                        if let Ok(s) = dwarf.attr_string(unit, na.value()) {
544                                            if let Ok(s_str) = s.to_string_lossy() {
545                                                v_name = s_str.into_owned();
546                                            }
547                                        }
548                                    }
549                                    let mut v_val: i64 = 0;
550                                    if let Some(cv) = ce.attr(gimli::DW_AT_const_value) {
551                                        let signed = match &base_type {
552                                            TypeInfo::BaseType { encoding, .. } => {
553                                                *encoding
554                                                    == gimli::constants::DW_ATE_signed.0 as u16
555                                                    || *encoding
556                                                        == gimli::constants::DW_ATE_signed_char.0
557                                                            as u16
558                                            }
559                                            TypeInfo::TypedefType {
560                                                underlying_type, ..
561                                            }
562                                            | TypeInfo::QualifiedType {
563                                                underlying_type, ..
564                                            } => {
565                                                matches!(
566                                                    &**underlying_type,
567                                                    TypeInfo::BaseType { encoding, .. }
568                                                        if *encoding == gimli::constants::DW_ATE_signed.0 as u16
569                                                            || *encoding
570                                                                == gimli::constants::DW_ATE_signed_char.0 as u16
571                                                )
572                                            }
573                                            _ => true,
574                                        };
575                                        v_val = match cv.value() {
576                                            gimli::AttributeValue::Udata(u) => u as i64,
577                                            gimli::AttributeValue::Sdata(s) => s,
578                                            gimli::AttributeValue::Data1(b) => {
579                                                let u = b as u64;
580                                                if signed && (u & 0x80) != 0 {
581                                                    (u as i8) as i64
582                                                } else {
583                                                    u as i64
584                                                }
585                                            }
586                                            gimli::AttributeValue::Data2(u) => {
587                                                let u = u as u64;
588                                                if signed && (u & 0x8000) != 0 {
589                                                    (u as i16) as i64
590                                                } else {
591                                                    u as i64
592                                                }
593                                            }
594                                            gimli::AttributeValue::Data4(u) => {
595                                                let u = u as u64;
596                                                if signed && (u & 0x8000_0000) != 0 {
597                                                    (u as i32) as i64
598                                                } else {
599                                                    u as i64
600                                                }
601                                            }
602                                            gimli::AttributeValue::Data8(u) => u as i64,
603                                            _ => v_val,
604                                        };
605                                    }
606                                    if v_name.is_empty() {
607                                        v_name = format!("variant_{}", variants.len());
608                                    }
609                                    variants.push(crate::EnumVariant {
610                                        name: v_name,
611                                        value: v_val,
612                                    });
613                                }
614                            }
615                        }
616                    }
617                    return Some(TypeInfo::EnumType {
618                        name,
619                        size: byte_size,
620                        base_type: Box::new(base_type),
621                        variants,
622                    });
623                }
624                DW_TAG_ARRAY_TYPE => {
625                    // element_type shallow + total_size if available + subrange element_count (one step deeper)
626                    let mut elem_type: Option<TypeInfo> = None;
627                    if let Some(gimli::AttributeValue::UnitRef(eoff)) = entry.attr_value(DW_AT_TYPE)
628                    {
629                        elem_type = Self::resolve_type_shallow_at_offset(dwarf, unit, eoff);
630                    }
631                    let element_type = Box::new(elem_type.unwrap_or(TypeInfo::UnknownType {
632                        name: "<elem>".to_string(),
633                    }));
634                    let mut total_size: Option<u64> = None;
635                    if let Some(a) = entry.attr(DW_AT_BYTE_SIZE) {
636                        if let gimli::AttributeValue::Udata(sz) = a.value() {
637                            total_size = Some(sz);
638                        }
639                    }
640                    // Optional: subrange child yields count/upper_bound
641                    let mut element_count: Option<u64> = None;
642                    if let Ok(mut tree) = unit.entries_tree(Some(entry.offset())) {
643                        if let Ok(root) = tree.root() {
644                            let mut children = root.children();
645                            while let Ok(Some(child)) = children.next() {
646                                let ce = child.entry();
647                                if ce.tag() == gimli::DW_TAG_subrange_type {
648                                    // Prefer DW_AT_count; fallback to upper_bound (+1)
649                                    if let Some(cv) = ce.attr(gimli::DW_AT_count) {
650                                        match cv.value() {
651                                            gimli::AttributeValue::Udata(u) => {
652                                                element_count = Some(u);
653                                            }
654                                            gimli::AttributeValue::Sdata(s) => {
655                                                if s >= 0 {
656                                                    element_count = Some(s as u64);
657                                                }
658                                            }
659                                            gimli::AttributeValue::Data1(b) => {
660                                                element_count = Some(b as u64);
661                                            }
662                                            gimli::AttributeValue::Data2(u) => {
663                                                element_count = Some(u as u64);
664                                            }
665                                            gimli::AttributeValue::Data4(u) => {
666                                                element_count = Some(u as u64);
667                                            }
668                                            gimli::AttributeValue::Data8(u) => {
669                                                element_count = Some(u);
670                                            }
671                                            _ => {}
672                                        }
673                                    }
674                                    if element_count.is_none() {
675                                        if let Some(ub) = ce.attr(gimli::DW_AT_upper_bound) {
676                                            let ub_v: Option<i64> = match ub.value() {
677                                                gimli::AttributeValue::Udata(u) => Some(u as i64),
678                                                gimli::AttributeValue::Sdata(s) => Some(s),
679                                                gimli::AttributeValue::Data1(b) => Some(b as i64),
680                                                gimli::AttributeValue::Data2(u) => Some(u as i64),
681                                                gimli::AttributeValue::Data4(u) => Some(u as i64),
682                                                gimli::AttributeValue::Data8(u) => Some(u as i64),
683                                                _ => None,
684                                            };
685                                            if let Some(ub_i) = ub_v {
686                                                if ub_i >= 0 {
687                                                    element_count = Some((ub_i as u64) + 1);
688                                                }
689                                            }
690                                        }
691                                    }
692                                    // Stop at first subrange
693                                    if element_count.is_some() {
694                                        break;
695                                    }
696                                }
697                            }
698                        }
699                    }
700                    // If total_size absent but count + elem size known, compute it
701                    if total_size.is_none() {
702                        let es = element_type.size();
703                        if let Some(cnt) = element_count {
704                            if es > 0 {
705                                total_size = Some(es * cnt);
706                            }
707                        }
708                    }
709                    return Some(TypeInfo::ArrayType {
710                        element_type,
711                        element_count,
712                        total_size,
713                    });
714                }
715                DW_TAG_SUBROUTINE_TYPE => {
716                    return Some(TypeInfo::FunctionType {
717                        return_type: None,
718                        parameters: Vec::new(),
719                    });
720                }
721                _ => {
722                    // Fallback: return alias name or entry name
723                    let nm = alias_name
724                        .or(entry_name)
725                        .unwrap_or_else(|| "<unknown>".to_string());
726                    return Some(TypeInfo::UnknownType { name: nm });
727                }
728            }
729        }
730    }
731
732    // Full variable collection and traversal helpers removed in shallow-only mode
733
734    // parse_variable_entry wrapper removed; use parse_variable_entry_with_mode
735
736    /// Parse a variable and optionally skip full DWARF type resolution
737    #[allow(clippy::too_many_arguments)]
738    pub fn parse_variable_entry_with_mode(
739        &self,
740        entry: &gimli::DebuggingInformationEntry<DwarfReader>,
741        unit: &gimli::Unit<DwarfReader>,
742        dwarf: &gimli::Dwarf<DwarfReader>,
743        address: u64,
744        get_cfa: Option<&dyn Fn(u64) -> Result<Option<crate::core::CfaResult>>>,
745        function_context: Option<&FunctionBlocks>,
746        cfi_index: Option<&CfiIndex>,
747        scope_depth: usize,
748    ) -> Result<Option<VariableWithEvaluation>> {
749        // No traversal context retained in shallow mode
750        // Resolve basic
751        let mut visited = std::collections::HashSet::new();
752        let Some(name) = Self::resolve_name_with_origins(entry, unit, dwarf, &mut visited)? else {
753            return Ok(None);
754        };
755        let is_parameter = entry.tag() == gimli::constants::DW_TAG_formal_parameter;
756        let type_name = Self::resolve_type_name(entry, unit, dwarf)?;
757        let evaluation_result = self.parse_location(
758            entry,
759            unit,
760            dwarf,
761            address,
762            get_cfa,
763            function_context,
764            cfi_index,
765        )?;
766        // Full type resolution disabled in shallow mode
767        let dwarf_type = None;
768        Ok(Some(VariableWithEvaluation {
769            name,
770            type_name,
771            dwarf_type,
772            evaluation_result,
773            scope_depth,
774            is_parameter,
775            is_artificial: false,
776        }))
777    }
778
779    /// Resolve type name for a variable or type DIE.
780    ///
781    /// This function follows DW_AT_type chains (pointer/const/array/typedef) and
782    /// includes a recursion guard to break true cycles (e.g., typedef A->B->A).
783    fn resolve_type_name(
784        entry: &gimli::DebuggingInformationEntry<DwarfReader>,
785        unit: &gimli::Unit<DwarfReader>,
786        dwarf: &gimli::Dwarf<DwarfReader>,
787    ) -> Result<String> {
788        let mut visited_types: HashSet<gimli::UnitOffset> = HashSet::new();
789        Self::resolve_type_name_rec(entry, unit, dwarf, &mut visited_types)
790    }
791
792    fn resolve_type_name_rec(
793        entry: &gimli::DebuggingInformationEntry<DwarfReader>,
794        unit: &gimli::Unit<DwarfReader>,
795        dwarf: &gimli::Dwarf<DwarfReader>,
796        visited: &mut HashSet<gimli::UnitOffset>,
797    ) -> Result<String> {
798        // Follow DW_AT_type if present
799        let Some(type_off) = Self::resolve_type_ref(entry, unit, dwarf)? else {
800            // As a fallback, try to use the entry's own name if any
801            let mut name_visited = HashSet::new();
802            if let Some(n) = Self::resolve_name_with_origins(entry, unit, dwarf, &mut name_visited)?
803            {
804                return Ok(n);
805            }
806            return Ok("unknown".to_string());
807        };
808
809        // Recursion guard: if we've seen this type offset already, break the cycle
810        if !visited.insert(type_off) {
811            return Ok("<recursive>".to_string());
812        }
813
814        let mut tree = unit.entries_tree(Some(type_off))?;
815        let type_node = tree.root()?;
816        let type_entry = type_node.entry();
817
818        // If this DIE has a name, prefer it directly
819        let mut name_visited = HashSet::new();
820        if let Some(name) =
821            Self::resolve_name_with_origins(type_entry, unit, dwarf, &mut name_visited)?
822        {
823            return Ok(name);
824        }
825
826        // Handle wrapper/indirection DIEs by following their DW_AT_type
827        match type_entry.tag() {
828            gimli::constants::DW_TAG_pointer_type => {
829                let pointee = Self::resolve_type_name_rec(type_entry, unit, dwarf, visited)?;
830                Ok(format!("{pointee}*"))
831            }
832            gimli::constants::DW_TAG_const_type => {
833                let base = Self::resolve_type_name_rec(type_entry, unit, dwarf, visited)?;
834                Ok(format!("const {base}"))
835            }
836            gimli::constants::DW_TAG_array_type => {
837                let elem = Self::resolve_type_name_rec(type_entry, unit, dwarf, visited)?;
838                Ok(format!("{elem}[]"))
839            }
840            gimli::constants::DW_TAG_typedef => {
841                // Use typedef's own name if present; otherwise follow underlying type
842                let mut tvisited = HashSet::new();
843                if let Some(tname) =
844                    Self::resolve_name_with_origins(type_entry, unit, dwarf, &mut tvisited)?
845                {
846                    Ok(tname)
847                } else {
848                    Self::resolve_type_name_rec(type_entry, unit, dwarf, visited)
849                }
850            }
851            // Fallback: stringify the DWARF tag
852            other => Ok(format!("{other:?}")),
853        }
854    }
855
856    /// Parse location attribute
857    #[allow(clippy::too_many_arguments)]
858    pub fn parse_location(
859        &self,
860        entry: &gimli::DebuggingInformationEntry<DwarfReader>,
861        unit: &gimli::Unit<DwarfReader>,
862        dwarf: &gimli::Dwarf<DwarfReader>,
863        address: u64,
864        get_cfa: Option<&dyn Fn(u64) -> Result<Option<crate::core::CfaResult>>>,
865        function_context: Option<&FunctionBlocks>,
866        cfi_index: Option<&CfiIndex>,
867    ) -> Result<EvaluationResult> {
868        // Use ExpressionEvaluator for unified logic
869        ExpressionEvaluator::evaluate_location(
870            entry,
871            unit,
872            dwarf,
873            address,
874            get_cfa,
875            function_context,
876            cfi_index,
877        )
878    }
879
880    // extract_name removed; call resolve_name_with_origins directly when needed
881
882    fn resolve_name_with_origins(
883        entry: &gimli::DebuggingInformationEntry<DwarfReader>,
884        unit: &gimli::Unit<DwarfReader>,
885        dwarf: &gimli::Dwarf<DwarfReader>,
886        _visited: &mut HashSet<gimli::UnitOffset>,
887    ) -> Result<Option<String>> {
888        Ok(resolve_name_with_origins(dwarf, unit, entry)?)
889    }
890
891    fn resolve_type_ref(
892        entry: &gimli::DebuggingInformationEntry<DwarfReader>,
893        unit: &gimli::Unit<DwarfReader>,
894        dwarf: &gimli::Dwarf<DwarfReader>,
895    ) -> Result<Option<gimli::UnitOffset>> {
896        resolve_type_ref_in_same_unit_with_origins(dwarf, entry, unit)
897    }
898
899    // resolve_flag_with_origins and entry_pc_matches removed with variable traversal helpers
900}
901
902impl Default for DetailedParser {
903    fn default() -> Self {
904        Self::new()
905    }
906}