neo_solidity/ir/expressions/
variable.rs

1fn lower_variable_expression(
2    identifier: &Identifier,
3    ctx: &mut LoweringContext,
4    instructions: &mut Vec<Instruction>,
5) -> bool {
6    if identifier.name == "this" {
7        instructions.push(Instruction::CallBuiltin {
8            builtin: BuiltinCall::Syscall("System.Runtime.GetExecutingScriptHash".to_string()),
9            arg_count: 0,
10        });
11        return true;
12    }
13    if identifier.name == "block" || identifier.name == "msg" {
14        instructions.push(Instruction::PushLiteral(LiteralValue::Integer(BigInt::zero())));
15        return true;
16    }
17    if identifier.name == "super" {
18        // `super` is only meaningful as `super.method()`. When used as a bare
19        // identifier (e.g., assigned to a variable), emit a targeted diagnostic
20        // instead of the generic "unknown identifier" error.
21        ctx.record_error_with_suggestion(
22            "the 'super' keyword can only be used in member-call expressions (super.method())",
23            "use super.methodName() to call a parent contract's function, or inline the parent logic directly",
24        );
25        instructions.push(Instruction::PushLiteral(LiteralValue::Integer(BigInt::zero())));
26        return false;
27    }
28    if let Some(alias) = ctx.storage_alias(&identifier.name).cloned() {
29        return emit_storage_load(&alias, ctx, instructions);
30    }
31    if let Some(index) = ctx.param_index_map.get(&identifier.name) {
32        instructions.push(Instruction::LoadParameter(*index));
33        true
34    } else if let Some(index) = ctx.resolve_local(&identifier.name) {
35        instructions.push(Instruction::LoadLocal(index));
36        true
37    } else if let Some(index) = ctx.state_index_map.get(&identifier.name) {
38        let state_index = *index;
39        let state_type = ctx.state_type(state_index).cloned();
40        let const_initializer = ctx.state_metadata(*index).and_then(|meta| {
41            if meta.is_constant {
42                meta.initializer.clone()
43            } else {
44                None
45            }
46        });
47        if let Some(initializer) = const_initializer {
48            // Preserve hash160 constants as address literals so downstream manifest
49            // permission inference can recover exact contract hashes for
50            // `Syscalls.contractCall(CONSTANT_HASH, ...)` patterns.
51            if matches!(state_type.as_ref(), Some(ValueType::Address)) {
52                if let Some(bytes) = address_bytes_le_from_expression(&initializer) {
53                    instructions.push(Instruction::PushLiteral(LiteralValue::Address(bytes)));
54                    return true;
55                }
56            }
57
58            return lower_expression(&initializer, ctx, instructions);
59        }
60
61        // Struct-typed state variables are stored field-by-field in derived slots. Loading the
62        // base slot does not materialize the struct value, so build it by reading each field.
63        if let Some(vt @ ValueType::Struct { .. }) = state_type {
64            let reference = StorageReference {
65                state_index,
66                key_expressions: Vec::new(),
67                key_types: Vec::new(),
68                value_type: vt,
69                field_path: Vec::new(),
70            };
71            return emit_storage_load(&reference, ctx, instructions);
72        }
73
74        instructions.push(Instruction::LoadState(state_index));
75        true
76    } else {
77        ctx.record_error_with_suggestion(
78            format!("unknown identifier '{}'", identifier.name),
79            "check spelling or ensure the variable is declared in the same contract or an imported library",
80        );
81        false
82    }
83}