wat_service 0.3.0

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

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

pub fn check(
    diags: &mut Vec<Diagnostic>,
    service: &LanguageService,
    uri: InternUri,
    line_index: &LineIndex,
    symbol_table: &SymbolTable,
    node: &SyntaxNode,
) {
    let Some(instr) = PlainInstr::cast(node.clone()) else {
        return;
    };
    if instr
        .instr_name()
        .is_none_or(|name| name.text() != "br_table")
    {
        return;
    }

    let mut immediates = instr.immediates();
    let Some(expected) = immediates
        .next()
        .map(|immediate| resolve_br_types(service, uri, symbol_table, &immediate))
    else {
        return;
    };
    diags.extend(immediates.filter_map(|immediate| {
        let received = resolve_br_types(service, uri, 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(NumberOrString::String(DIAGNOSTIC_CODE.into())),
                message: format!(
                    "type mismatch in `br_table`: expected [{}], found [{}]",
                    expected.iter().join(", "),
                    received.iter().join(", ")
                ),
                ..Default::default()
            })
        } else {
            None
        }
    }));
}