use std::collections::HashSet;
use log::debug;
use rue_ast::{AstNode, AstStructBinding};
use rue_diagnostic::DiagnosticKind;
use rue_hir::{BindingSymbol, Declaration, Hir, Symbol, SymbolId, SymbolPath, Value};
use crate::{
Compiler, CompletionContext, Field, FieldResult, SyntaxField, SyntaxItemKind, compile_field,
create_binding, create_binding_for_identifier,
};
pub fn create_struct_binding(
ctx: &mut Compiler,
symbol: SymbolId,
struct_binding: &AstStructBinding,
) {
let ty = ctx.symbol_type(symbol);
let reference = Value::new(ctx.alloc_hir(Hir::Reference(symbol)), ty)
.with_reference(SymbolPath::new(symbol, vec![]));
let mut specified_fields = HashSet::new();
let len = struct_binding.fields().count();
for (i, field) in struct_binding.fields().enumerate() {
let Some(name) = field.name() else {
continue;
};
if let Some(spread) = field.spread() {
if i != len - 1 {
ctx.diagnostic(&spread, DiagnosticKind::NonFinalSpread);
}
let binding_symbol = ctx.alloc_symbol(Symbol::Binding(BindingSymbol {
name: None,
value: reference.clone(),
inline: true,
}));
ctx.push_declaration(Declaration::Symbol(binding_symbol));
ctx.reference(Declaration::Symbol(symbol), None);
create_binding_for_identifier(ctx, binding_symbol, &name);
ctx.pop_declaration();
continue;
}
specified_fields.insert(name.text().to_string());
let value = match compile_field(ctx, reference.clone(), &Field::Named(name.text())) {
FieldResult::Value(value) => value,
FieldResult::Unknown => {
debug!("Unresolved field access due to unknown field");
let type_name = ctx.type_name(reference.ty);
ctx.diagnostic(
&name,
DiagnosticKind::UnknownField(name.text().to_string(), type_name),
);
ctx.builtins().unresolved.clone()
}
FieldResult::Error => {
debug!("Unresolved field access due to missing field in underlying struct type");
let type_name = ctx.type_name(reference.ty);
ctx.diagnostic(
&name,
DiagnosticKind::MissingField(name.text().to_string(), type_name),
);
ctx.builtins().unresolved.clone()
}
};
let field_type = value.ty;
let binding_symbol = ctx.alloc_symbol(Symbol::Binding(BindingSymbol {
name: None,
value,
inline: true,
}));
ctx.push_declaration(Declaration::Symbol(binding_symbol));
ctx.reference(Declaration::Symbol(symbol), None);
if let Some(binding) = field.binding() {
create_binding(ctx, binding_symbol, &binding);
ctx.add_syntax(
SyntaxItemKind::FieldReference(SyntaxField {
name: name.text().to_string(),
container: ty,
ty: field_type,
}),
name.text_range(),
);
} else {
create_binding_for_identifier(ctx, binding_symbol, &name);
}
ctx.pop_declaration();
}
ctx.add_syntax(
SyntaxItemKind::CompletionContext(CompletionContext::StructFields {
ty,
specified_fields: Some(specified_fields),
}),
struct_binding.syntax().text_range(),
);
}