neo_solidity/ir/expressions/calls/
variable_calls.rs1fn try_lower_variable_call(
2 func: &Expression,
3 args: &[Expression],
4 ctx: &mut LoweringContext,
5 instructions: &mut Vec<Instruction>,
6) -> Option<bool> {
7 if let Expression::Variable(identifier) = func {
8 if identifier.name == "require" || identifier.name == "assert" {
9 ctx.record_error(format!("{}() cannot be used as an expression", identifier.name));
10 return Some(false);
11 }
12
13 if identifier.name == "selfdestruct" {
14 eprintln!(
18 "warning: selfdestruct() auto-mapped to ContractManagement.destroy() \
19 on Neo N3. The recipient address argument is ignored — Neo does not \
20 transfer remaining funds on destroy. Use NativeCalls.gasTransfer() \
21 to move funds before destroying."
22 );
23 if args.len() == 1 {
24 if !lower_expression(&args[0], ctx, instructions) {
26 return Some(false);
27 }
28 instructions.push(Instruction::Drop(ValueType::Any));
29 }
30 instructions.push(Instruction::CallBuiltin {
31 builtin: BuiltinCall::NativeCall {
32 contract: NativeContract::ContractManagement,
33 method: "destroy".to_string(),
34 },
35 arg_count: 0,
36 });
37 return Some(false); }
39
40 if identifier.name == "blockhash" {
41 eprintln!(
43 "warning: blockhash() auto-mapped to Ledger.getBlockHash() \
44 on Neo N3. Returns the block hash for the given index."
45 );
46 if args.len() == 1 {
47 if !lower_expression(&args[0], ctx, instructions) {
48 return Some(false);
49 }
50 } else {
51 ctx.record_error("blockhash() requires exactly 1 argument");
52 return Some(false);
53 }
54 instructions.push(Instruction::CallBuiltin {
55 builtin: BuiltinCall::NativeCall {
56 contract: NativeContract::Ledger,
57 method: "getBlockHash".to_string(),
58 },
59 arg_count: 1,
60 });
61 return Some(true);
62 }
63
64 if identifier.name == "gasleft" {
65 instructions.push(Instruction::CallBuiltin {
67 builtin: BuiltinCall::Syscall(
68 "System.Runtime.GasLeft".to_string(),
69 ),
70 arg_count: 0,
71 });
72 return Some(true);
73 }
74
75 if args.len() == 1 && ctx.is_contract_type_name(&identifier.name) {
78 if matches!(
79 infer_type_from_expression(&args[0], ctx),
80 Some(ValueType::Address)
81 ) {
82 return Some(lower_expression(&args[0], ctx, instructions));
83 }
84
85 if let Some(bytes) = address_bytes_le_from_expression(&args[0]) {
86 instructions.push(Instruction::PushLiteral(LiteralValue::Address(bytes)));
87 return Some(true);
88 }
89 }
90
91 if ctx.function_names.contains(&identifier.name) {
92 let mut success = true;
93 for arg in args {
94 if !lower_expression(arg, ctx, instructions) {
95 success = false;
96 }
97 }
98
99 if success {
100 if let Some(neo_name) = ctx.neo_function_name(&identifier.name, args.len()) {
101 instructions.push(Instruction::CallFunction {
102 name: neo_name,
103 arg_count: args.len(),
104 });
105 } else {
106 ctx.record_error(format!(
107 "no overload of '{}' with {} argument(s)",
108 identifier.name,
109 args.len()
110 ));
111 success = false;
112 }
113 }
114
115 if ctx.is_void_function(&identifier.name) {
118 return Some(false);
119 }
120 return Some(success);
121 }
122
123 ctx.record_error_with_suggestion(
124 format!("unsupported function call '{}'", identifier.name),
125 "check spelling or ensure the function is declared in the same contract",
126 );
127 return Some(false);
128 }
129
130 None
131}