Skip to main content

rue_compiler/compile/binding/
struct_binding.rs

1use std::collections::HashSet;
2
3use log::debug;
4use rue_ast::{AstNode, AstStructBinding};
5use rue_diagnostic::DiagnosticKind;
6use rue_hir::{BindingSymbol, Declaration, Hir, Symbol, SymbolId, SymbolPath, Value};
7
8use crate::{
9    Compiler, CompletionContext, Field, FieldResult, SyntaxField, SyntaxItemKind, compile_field,
10    create_binding, create_binding_for_identifier,
11};
12
13pub fn create_struct_binding(
14    ctx: &mut Compiler,
15    symbol: SymbolId,
16    struct_binding: &AstStructBinding,
17) {
18    let ty = ctx.symbol_type(symbol);
19    let reference = Value::new(ctx.alloc_hir(Hir::Reference(symbol)), ty)
20        .with_reference(SymbolPath::new(symbol, vec![]));
21
22    let mut specified_fields = HashSet::new();
23
24    let len = struct_binding.fields().count();
25
26    for (i, field) in struct_binding.fields().enumerate() {
27        let Some(name) = field.name() else {
28            continue;
29        };
30
31        if let Some(spread) = field.spread() {
32            if i != len - 1 {
33                ctx.diagnostic(&spread, DiagnosticKind::NonFinalSpread);
34            }
35
36            let binding_symbol = ctx.alloc_symbol(Symbol::Binding(BindingSymbol {
37                name: None,
38                value: reference.clone(),
39                inline: true,
40            }));
41
42            ctx.push_declaration(Declaration::Symbol(binding_symbol));
43
44            ctx.reference(Declaration::Symbol(symbol), None);
45
46            create_binding_for_identifier(ctx, binding_symbol, &name);
47
48            ctx.pop_declaration();
49
50            continue;
51        }
52
53        specified_fields.insert(name.text().to_string());
54
55        let value = match compile_field(ctx, reference.clone(), &Field::Named(name.text())) {
56            FieldResult::Value(value) => value,
57            FieldResult::Unknown => {
58                debug!("Unresolved field access due to unknown field");
59                let type_name = ctx.type_name(reference.ty);
60                ctx.diagnostic(
61                    &name,
62                    DiagnosticKind::UnknownField(name.text().to_string(), type_name),
63                );
64                ctx.builtins().unresolved.clone()
65            }
66            FieldResult::Error => {
67                debug!("Unresolved field access due to missing field in underlying struct type");
68                let type_name = ctx.type_name(reference.ty);
69                ctx.diagnostic(
70                    &name,
71                    DiagnosticKind::MissingField(name.text().to_string(), type_name),
72                );
73                ctx.builtins().unresolved.clone()
74            }
75        };
76
77        let field_type = value.ty;
78
79        let binding_symbol = ctx.alloc_symbol(Symbol::Binding(BindingSymbol {
80            name: None,
81            value,
82            inline: true,
83        }));
84
85        ctx.push_declaration(Declaration::Symbol(binding_symbol));
86
87        ctx.reference(Declaration::Symbol(symbol), None);
88
89        if let Some(binding) = field.binding() {
90            create_binding(ctx, binding_symbol, &binding);
91
92            ctx.add_syntax(
93                SyntaxItemKind::FieldReference(SyntaxField {
94                    name: name.text().to_string(),
95                    container: ty,
96                    ty: field_type,
97                }),
98                name.text_range(),
99            );
100        } else {
101            create_binding_for_identifier(ctx, binding_symbol, &name);
102        }
103
104        ctx.pop_declaration();
105    }
106
107    ctx.add_syntax(
108        SyntaxItemKind::CompletionContext(CompletionContext::StructFields {
109            ty,
110            specified_fields: Some(specified_fields),
111        }),
112        struct_binding.syntax().text_range(),
113    );
114}