neo-decompiler 0.11.0

Neo N3 NEF decompiler: parse, disassemble, lift bytecode to high-level pseudocode and C# skeletons, with a CLI, JSON reports, and optional WebAssembly bindings.
Documentation
/// Inferred C# type strings for argument and body-local slots of a single method.
///
/// Built from [`crate::decompiler::analysis::types::TypeInfo`] for the method
/// being rendered. Entries are empty (`""`) when no type was inferred, in which
/// case the declaration falls back to `var`.
#[derive(Debug, Clone, Default)]
pub(in crate::decompiler) struct SlotTypes {
    /// C# type name per argument-slot index, or `""` when unknown.
    pub arguments: Vec<&'static str>,
    /// C# type name per local-slot index, or `""` when unknown.
    pub locals: Vec<&'static str>,
    /// C# type name per static-field-slot index, or `""` when unknown.
    pub statics: Vec<&'static str>,
}

impl SlotTypes {
    pub(in crate::decompiler) fn argument_type(&self, index: usize) -> Option<&'static str> {
        self.arguments
            .get(index)
            .copied()
            .filter(|ty| !ty.is_empty())
    }

    /// Resolve the C# declaration type for a slot name emitted by the lifter
    /// (`loc3`, `static1`). Returns `Some("int")`-style only when a non-empty
    /// type was inferred; returns `None` for temps (`t0`), arguments, and
    /// unknown slots so the caller emits `var`.
    pub(super) fn declaration_type(&self, name: &str) -> Option<&'static str> {
        let (slots, idx) = if let Some(rest) = name.strip_prefix("loc") {
            (&self.locals, parse_slot_index(rest)?)
        } else if let Some(rest) = name.strip_prefix("static") {
            (&self.statics, parse_slot_index(rest)?)
        } else {
            return None;
        };
        slots.get(idx).copied().filter(|ty| !ty.is_empty())
    }
}

/// Parse the trailing digit run of a slot name (`"3"` from `"loc3"`) as a slot
/// index. Returns `None` unless the remainder is all-ASCII-digits so that
/// unrelated identifiers never accidentally match.
fn parse_slot_index(rest: &str) -> Option<usize> {
    if rest.is_empty() || !rest.bytes().all(|b| b.is_ascii_digit()) {
        return None;
    }
    rest.parse::<usize>().ok()
}