neo-decompiler 0.10.1

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
/// Returns true when `rhs` is safe to drop without altering side effects
/// or hiding a runtime exception the original bytecode would have raised.
pub(super) fn is_pure_rhs(rhs: &str) -> bool {
    let trimmed = rhs.trim();
    if trimmed.is_empty() {
        return false;
    }
    if trimmed
        .as_bytes()
        .iter()
        .any(|b| matches!(*b, b'/' | b'%' | b'['))
    {
        return false;
    }
    if !trimmed.contains('(') {
        return true;
    }
    rhs_calls_only_pure_helpers(trimmed)
}

/// Return `true` when every `name(` call in `expr` starts with a
/// known-pure helper identifier.
fn rhs_calls_only_pure_helpers(expr: &str) -> bool {
    let bytes = expr.as_bytes();
    let mut in_string: Option<u8> = None;
    let mut i = 0;
    while i < bytes.len() {
        let b = bytes[i];
        if let Some(quote) = in_string {
            if b == b'\\' && i + 1 < bytes.len() {
                i += 2;
                continue;
            }
            if b == quote {
                in_string = None;
            }
            i += 1;
            continue;
        }
        if b == b'"' || b == b'\'' {
            in_string = Some(b);
            i += 1;
            continue;
        }
        if b == b'(' {
            let mut start = i;
            while start > 0 {
                let prev = bytes[start - 1];
                if prev.is_ascii_alphanumeric() || prev == b'_' {
                    start -= 1;
                } else {
                    break;
                }
            }
            if start == i {
                i += 1;
                continue;
            }
            let ident = &expr[start..i];
            if !is_pure_helper_identifier(ident) {
                return false;
            }
        }
        i += 1;
    }
    true
}

fn is_pure_helper_identifier(ident: &str) -> bool {
    if matches!(
        ident,
        "abs"
            | "sign"
            | "sqrt"
            | "min"
            | "max"
            | "pow"
            | "modpow"
            | "modmul"
            | "within"
            | "left"
            | "right"
            | "substr"
            | "is_null"
            | "keys"
            | "values"
            | "has_key"
            | "len"
    ) {
        return true;
    }
    ident.starts_with("is_type_") || ident.starts_with("convert_to_")
}