wat_service 0.7.0

WebAssembly Text Format language service.
Documentation
use crate::{
    LanguageService,
    binder::{SymbolKey, SymbolKind, SymbolTable},
    document::Document,
    helpers,
    types_analyzer::{self, CompositeType, FieldType, Fields, StorageType},
};
use line_index::LineIndex;
use lspt::{Diagnostic, DiagnosticRelatedInformation, DiagnosticSeverity, Location, Union2};
use rowan::ast::support;
use wat_syntax::{SyntaxKind, SyntaxNode};

const DIAGNOSTIC_CODE: &str = "new-non-defaultable";

pub fn check(
    service: &LanguageService,
    document: Document,
    line_index: &LineIndex,
    symbol_table: &SymbolTable,
    node: &SyntaxNode,
) -> Option<Diagnostic> {
    let def_types = types_analyzer::get_def_types(service, document);
    let immediate = node.first_child()?;
    let instr_name = support::token(node, SyntaxKind::INSTR_NAME)?;
    if !instr_name.text().ends_with(".new_default") {
        return None;
    }
    let def_symbol = symbol_table.find_def(SymbolKey::new(&immediate))?;
    match &def_types.get(&def_symbol.key)?.comp {
        CompositeType::Struct(Fields(fields)) => {
            let non_defaultables = fields
                .iter()
                .filter_map(|field| match field {
                    (
                        FieldType {
                            storage: StorageType::Val(ty),
                            ..
                        },
                        idx,
                    ) if !ty.defaultable() => Some(idx),
                    _ => None,
                })
                .collect::<Vec<_>>();
            if non_defaultables.is_empty() {
                None
            } else {
                Some(Diagnostic {
                    range: helpers::rowan_range_to_lsp_range(line_index, immediate.text_range()),
                    severity: Some(DiagnosticSeverity::Error),
                    source: Some("wat".into()),
                    code: Some(Union2::B(DIAGNOSTIC_CODE.into())),
                    message: format!(
                        "struct type `{}` is not defaultable",
                        def_symbol.idx.render(service)
                    ),
                    related_information: Some(
                        non_defaultables
                            .into_iter()
                            .filter_map(|idx| {
                                symbol_table
                                    .symbols
                                    .values()
                                    .find(|symbol| {
                                        symbol.kind == SymbolKind::FieldDef
                                            && symbol.region == def_symbol.key
                                            && &symbol.idx == idx
                                    })
                                    .map(|symbol| DiagnosticRelatedInformation {
                                        location: Location {
                                            uri: document.uri(service).raw(service),
                                            range: helpers::rowan_range_to_lsp_range(
                                                line_index,
                                                symbol.key.text_range(),
                                            ),
                                        },
                                        message: format!(
                                            "field type `{}` is not defaultable",
                                            symbol.idx.render(service)
                                        ),
                                    })
                            })
                            .collect(),
                    ),
                    ..Default::default()
                })
            }
        }
        CompositeType::Array(Some(FieldType {
            storage: StorageType::Val(ty),
            ..
        })) if !ty.defaultable() => Some(Diagnostic {
            range: helpers::rowan_range_to_lsp_range(line_index, immediate.text_range()),
            severity: Some(DiagnosticSeverity::Error),
            source: Some("wat".into()),
            code: Some(Union2::B(DIAGNOSTIC_CODE.into())),
            message: format!(
                "array type `{}` is not defaultable",
                def_symbol.idx.render(service)
            ),
            ..Default::default()
        }),
        _ => None,
    }
}