neo_solidity/ir/expressions/member_access/
selectors.rs

1fn try_lower_selector_member_access(
2    inner: &Expression,
3    member: &Identifier,
4    ctx: &mut LoweringContext,
5    instructions: &mut Vec<Instruction>,
6) -> Option<bool> {
7    fn resolve_contract_type_name(expr: &Expression, ctx: &LoweringContext) -> Option<String> {
8        match expr {
9            Expression::Variable(type_name) if ctx.is_contract_type_name(&type_name.name) => {
10                Some(type_name.name.clone())
11            }
12            Expression::MemberAccess(_, namespace_expr, type_name)
13                if matches!(
14                    namespace_expr.as_ref(),
15                    Expression::Variable(namespace_id)
16                        if !ctx.param_index_map.contains_key(&namespace_id.name)
17                            && ctx.resolve_local(&namespace_id.name).is_none()
18                            && !ctx.state_index_map.contains_key(&namespace_id.name)
19                            && !ctx.is_contract_type_name(&namespace_id.name)
20                ) && ctx.is_contract_type_name(&type_name.name) =>
21            {
22                Some(type_name.name.clone())
23            }
24            _ => None,
25        }
26    }
27
28    if member.name != "selector" {
29        return None;
30    }
31
32    // Solidity exposes function selectors via `TypeName.method.selector` (bytes4).
33    // This is commonly used for interface receivers and low-level call encoding.
34    //
35    // The AST shape is: `MemberAccess(MemberAccess(Variable(TypeName), method), selector)`.
36    if let Expression::MemberAccess(_, target_inner, target_method) = inner {
37        if let Some(type_name) = resolve_contract_type_name(target_inner.as_ref(), ctx) {
38            if let Some(selectors) = ctx.type_method_selectors(&type_name, &target_method.name) {
39                if selectors.len() == 1 {
40                    instructions.push(Instruction::PushLiteral(
41                        LiteralValue::ByteArray(selectors[0].to_vec()),
42                    ));
43                    return Some(true);
44                }
45
46                ctx.record_error(format!(
47                    "ambiguous selector '{}.{}': {} overload(s)",
48                    type_name,
49                    target_method.name,
50                    selectors.len()
51                ));
52                return Some(false);
53            }
54
55            ctx.record_error(format!(
56                "unknown selector '{}.{}'",
57                type_name, target_method.name
58            ));
59            return Some(false);
60        }
61    }
62
63    ctx.record_error("unsupported selector expression");
64    Some(false)
65}