use brink_syntax::ast::{self, AstNode};
use super::super::context::{LowerScope, LowerSink, Lowered};
use super::super::doc_comment::{DocPolicy, parse_doc_comment};
use super::super::helpers::{make_name, name_from_ident};
use super::DeclareSymbols;
use crate::{DiagnosticCode, ListDecl, ListMember, SymbolKind};
impl DeclareSymbols for ast::ListDecl {
type Output = ListDecl;
fn declare_and_lower(
&self,
_scope: &LowerScope,
sink: &mut impl LowerSink,
) -> Lowered<ListDecl> {
let range = self.syntax().text_range();
let ident = self
.identifier()
.ok_or_else(|| sink.diagnose(range, DiagnosticCode::E008))?;
let name =
name_from_ident(&ident).ok_or_else(|| sink.diagnose(range, DiagnosticCode::E008))?;
let list_name_text = name.text.clone();
let (doc, issues) = parse_doc_comment(self.syntax(), DocPolicy::VALUE);
issues.diagnose(sink);
sink.declare_full(
SymbolKind::List,
&list_name_text,
name.range,
Vec::new(),
None,
doc,
);
let members: Vec<ListMember> = self
.definition()
.map(|def| {
def.members()
.filter_map(|m| lower_list_member(&m, range, sink))
.collect()
})
.unwrap_or_default();
for member in &members {
let qualified = format!("{list_name_text}.{}", member.name.text);
sink.declare(SymbolKind::ListItem, &qualified, member.name.range);
}
Ok(ListDecl {
ptr: ast::AstPtr::new(self),
name,
members,
})
}
}
fn lower_list_member(
m: &ast::ListMember,
_parent_range: rowan::TextRange,
sink: &mut impl LowerSink,
) -> Option<ListMember> {
let range = m.syntax().text_range();
if let Some(on) = m.on_member() {
let name_text = on.name().or_else(|| {
sink.diagnose(range, DiagnosticCode::E009);
None
})?;
#[expect(clippy::cast_possible_truncation, reason = "list values fit in i32")]
return Some(ListMember {
name: make_name(name_text, range),
value: on.value().map(|v| v as i32),
is_active: true,
});
}
if let Some(off) = m.off_member() {
let name_text = off.name().or_else(|| {
sink.diagnose(range, DiagnosticCode::E009);
None
})?;
#[expect(clippy::cast_possible_truncation, reason = "list values fit in i32")]
return Some(ListMember {
name: make_name(name_text, range),
value: off.value().map(|v| v as i32),
is_active: false,
});
}
sink.diagnose(range, DiagnosticCode::E009);
None
}