wat_service 0.9.0

WebAssembly Text Format language service.
Documentation
use super::Diagnostic;
use crate::data_set::CONST_INSTRS;
use rowan::{
    TextRange, WalkEvent,
    ast::{AstNode, support},
};
use wat_syntax::{SyntaxKind, SyntaxNode, ast::Instr};

const DIAGNOSTIC_CODE: &str = "const-expr";

pub fn check(node: &SyntaxNode) -> Option<Diagnostic> {
    let mut first = None;
    let mut last = None;
    let mut is_const = true;
    node.children()
        .filter(|child| Instr::can_cast(child.kind()))
        .for_each(|instr| {
            if first.is_none() {
                first = Some(instr.clone());
            }
            last = Some(instr.clone());
            let mut preorder = instr.preorder();
            while let Some(walk_event) = preorder.next() {
                let WalkEvent::Enter(node) = walk_event else {
                    continue;
                };
                match node.kind() {
                    SyntaxKind::BLOCK_BLOCK
                    | SyntaxKind::BLOCK_LOOP
                    | SyntaxKind::BLOCK_IF
                    | SyntaxKind::BLOCK_TRY_TABLE => {
                        is_const = false;
                        break;
                    }
                    SyntaxKind::PLAIN_INSTR => {
                        if support::token(&node, SyntaxKind::INSTR_NAME)
                            .is_some_and(|instr_name| !CONST_INSTRS.contains(&instr_name.text()))
                        {
                            is_const = false;
                            break;
                        }
                    }
                    _ => {
                        preorder.skip_subtree();
                    }
                }
            }
        });
    if !is_const
        && let Some(first) = first
        && let Some(last) = last
    {
        Some(Diagnostic {
            range: TextRange::cover(first.text_range(), last.text_range()),
            code: DIAGNOSTIC_CODE.into(),
            message: "expression must be constant".into(),
            ..Default::default()
        })
    } else {
        None
    }
}