From 16fcb3f7a410bdca50f2396c164ae56936564e99 Mon Sep 17 00:00:00 2001 From: Pig Fang Date: Wed, 8 Jan 2025 18:05:53 +0800 Subject: [PATCH] feat(service): report top-level error tokens --- crates/service/src/checker/mod.rs | 4 ++- crates/service/src/checker/syntax.rs | 33 +++++++++++++++++++ crates/service/src/files.rs | 6 ++-- ...ostics__syntax__top_level_error_token.snap | 25 ++++++++++++++ crates/service/tests/diagnostics/syntax.rs | 11 +++++++ crates/service/tests/diagnostics/unused.rs | 2 +- 6 files changed, 76 insertions(+), 5 deletions(-) create mode 100644 crates/service/src/checker/syntax.rs create mode 100644 crates/service/tests/diagnostics/snapshots/features__diagnostics__syntax__top_level_error_token.snap diff --git a/crates/service/src/checker/mod.rs b/crates/service/src/checker/mod.rs index d0054be7..f818b00f 100644 --- a/crates/service/src/checker/mod.rs +++ b/crates/service/src/checker/mod.rs @@ -7,6 +7,7 @@ mod immediates; mod implicit_module; mod multi_modules; mod shadow; +mod syntax; mod typeck; mod undef; mod unknown_instr; @@ -19,7 +20,8 @@ pub fn check(service: &LanguageService, uri: InternUri) -> Vec { let symbol_table = service.symbol_table(uri); let config = &service.get_config(uri); - let mut diagnostics = service.parser_result(uri).1; + let mut diagnostics = Vec::with_capacity(4); + syntax::check(service, &mut diagnostics, uri, &line_index, &root); root.descendants().for_each(|node| match node.kind() { SyntaxKind::ROOT => { multi_modules::check(&mut diagnostics, &line_index, &node); diff --git a/crates/service/src/checker/syntax.rs b/crates/service/src/checker/syntax.rs new file mode 100644 index 00000000..6776ac26 --- /dev/null +++ b/crates/service/src/checker/syntax.rs @@ -0,0 +1,33 @@ +use crate::{files::FilesCtx, helpers, InternUri, LanguageService}; +use line_index::LineIndex; +use lsp_types::{Diagnostic, DiagnosticSeverity, NumberOrString}; +use std::rc::Rc; +use wat_syntax::{SyntaxElement, SyntaxKind, SyntaxNode}; + +const DIAGNOSTIC_CODE: &str = "syntax"; + +pub fn check( + service: &LanguageService, + diags: &mut Vec, + uri: InternUri, + line_index: &LineIndex, + root: &SyntaxNode, +) { + let mut errors = Rc::unwrap_or_clone(service.parser_result(uri).1); + diags.append(&mut errors); + diags.extend( + root.children_with_tokens() + .filter_map(|element| match element { + SyntaxElement::Token(token) if token.kind() == SyntaxKind::ERROR => Some(token), + _ => None, + }) + .map(|token| Diagnostic { + range: helpers::rowan_range_to_lsp_range(line_index, token.text_range()), + severity: Some(DiagnosticSeverity::ERROR), + source: Some("wat".into()), + code: Some(NumberOrString::String(DIAGNOSTIC_CODE.into())), + message: "syntax error: unexpected token".into(), + ..Default::default() + }), + ); +} diff --git a/crates/service/src/files.rs b/crates/service/src/files.rs index 1e9338cd..0d44d756 100644 --- a/crates/service/src/files.rs +++ b/crates/service/src/files.rs @@ -19,7 +19,7 @@ pub(crate) trait FilesCtx: salsa::Database { #[salsa::memoized] #[salsa::invoke(parse)] - fn parser_result(&self, uri: InternUri) -> (GreenNode, Vec); + fn parser_result(&self, uri: InternUri) -> (GreenNode, Rc>); #[salsa::memoized] fn root(&self, uri: InternUri) -> GreenNode; @@ -29,7 +29,7 @@ fn get_line_index(db: &dyn FilesCtx, uri: InternUri) -> Rc { Rc::new(LineIndex::new(&db.source(uri))) } -fn parse(db: &dyn FilesCtx, uri: InternUri) -> (GreenNode, Vec) { +fn parse(db: &dyn FilesCtx, uri: InternUri) -> (GreenNode, Rc>) { let source = db.source(uri); let line_index = db.line_index(uri); let mut parser = Parser::new(&source); @@ -60,7 +60,7 @@ fn parse(db: &dyn FilesCtx, uri: InternUri) -> (GreenNode, Vec) { } }) .collect(); - (green, syntax_errors) + (green, Rc::new(syntax_errors)) } fn root(db: &dyn FilesCtx, uri: InternUri) -> GreenNode { diff --git a/crates/service/tests/diagnostics/snapshots/features__diagnostics__syntax__top_level_error_token.snap b/crates/service/tests/diagnostics/snapshots/features__diagnostics__syntax__top_level_error_token.snap new file mode 100644 index 00000000..a25a0d63 --- /dev/null +++ b/crates/service/tests/diagnostics/snapshots/features__diagnostics__syntax__top_level_error_token.snap @@ -0,0 +1,25 @@ +--- +source: crates/service/tests/diagnostics/syntax.rs +expression: response +--- +{ + "kind": "full", + "items": [ + { + "range": { + "start": { + "line": 0, + "character": 8 + }, + "end": { + "line": 0, + "character": 9 + } + }, + "severity": 1, + "code": "syntax", + "source": "wat", + "message": "syntax error: unexpected token" + } + ] +} diff --git a/crates/service/tests/diagnostics/syntax.rs b/crates/service/tests/diagnostics/syntax.rs index 7b867922..f0d33d63 100644 --- a/crates/service/tests/diagnostics/syntax.rs +++ b/crates/service/tests/diagnostics/syntax.rs @@ -16,3 +16,14 @@ fn blocks() { let response = service.pull_diagnostics(create_params(uri)); assert_json_snapshot!(response); } + +#[test] +fn top_level_error_token() { + let uri = "untitled:test".parse::().unwrap(); + let source = "(module))"; + let mut service = LanguageService::default(); + service.commit(uri.clone(), source.into()); + calm(&mut service, uri.clone()); + let response = service.pull_diagnostics(create_params(uri)); + assert_json_snapshot!(response); +} diff --git a/crates/service/tests/diagnostics/unused.rs b/crates/service/tests/diagnostics/unused.rs index 58d4684a..81fa46e0 100644 --- a/crates/service/tests/diagnostics/unused.rs +++ b/crates/service/tests/diagnostics/unused.rs @@ -94,7 +94,7 @@ fn type_unused() { let source = r#" (module (type (func)) - (type $t (func)))) + (type $t (func))) "#; let mut service = LanguageService::default(); service.commit(uri.clone(), source.into());