wat_service 0.7.0

WebAssembly Text Format language service.
Documentation
use crate::{
    LanguageService, binder::SymbolTable, document::Document, helpers,
    types_analyzer::resolve_br_types,
};
use itertools::Itertools;
use line_index::LineIndex;
use lspt::{Diagnostic, DiagnosticSeverity, Union2};
use rowan::ast::AstNode;
use wat_syntax::{SyntaxNode, ast::PlainInstr};

const DIAGNOSTIC_CODE: &str = "br-table-branches";

pub fn check(
    diagnostics: &mut Vec<Diagnostic>,
    service: &LanguageService,
    document: Document,
    line_index: &LineIndex,
    symbol_table: &SymbolTable,
    node: &SyntaxNode,
) -> Option<()> {
    let instr = PlainInstr::cast(node.clone())?;
    if instr
        .instr_name()
        .is_none_or(|name| name.text() != "br_table")
    {
        return None;
    }

    let mut immediates = instr.immediates();
    let expected = immediates
        .next()
        .map(|immediate| resolve_br_types(service, document, symbol_table, &immediate))?;
    diagnostics.extend(immediates.filter_map(|immediate| {
        let received = resolve_br_types(service, document, symbol_table, &immediate);
        if received != expected {
            Some(Diagnostic {
                range: helpers::rowan_range_to_lsp_range(
                    line_index,
                    immediate.syntax().text_range(),
                ),
                severity: Some(DiagnosticSeverity::Error),
                source: Some("wat".into()),
                code: Some(Union2::B(DIAGNOSTIC_CODE.into())),
                message: format!(
                    "type mismatch in `br_table`: expected [{}], found [{}]",
                    expected.iter().map(|ty| ty.render(service)).join(", "),
                    received.iter().map(|ty| ty.render(service)).join(", ")
                ),
                ..Default::default()
            })
        } else {
            None
        }
    }));
    Some(())
}