Skip to content

Commit

Permalink
feat(service): detect mutating on immutable global
Browse files Browse the repository at this point in the history
  • Loading branch information
g-plane committed Jan 9, 2025
1 parent 34b9b0b commit f075b77
Show file tree
Hide file tree
Showing 6 changed files with 243 additions and 0 deletions.
75 changes: 75 additions & 0 deletions crates/service/src/checker/global_mut.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
use crate::{
binder::{SymbolItemKey, SymbolTable},
files::FilesCtx,
helpers, InternUri, LanguageService,
};
use line_index::LineIndex;
use lsp_types::{
Diagnostic, DiagnosticRelatedInformation, DiagnosticSeverity, Location, NumberOrString,
};
use rowan::ast::AstNode;
use wat_syntax::{
ast::{ModuleFieldGlobal, PlainInstr},
SyntaxNode,
};

const DIAGNOSTIC_CODE: &str = "global-mutation";

pub fn check(
service: &LanguageService,
diags: &mut Vec<Diagnostic>,
uri: InternUri,
line_index: &LineIndex,
root: &SyntaxNode,
symbol_table: &SymbolTable,
node: &SyntaxNode,
) {
let Some(instr) = PlainInstr::cast(node.clone()) else {
return;
};
match instr.instr_name() {
Some(name) if name.text() == "global.set" => {}
_ => return,
}
diags.extend(instr.immediates().filter_map(|immediate| {
let defs = symbol_table.find_defs(SymbolItemKey::new(immediate.syntax()))?;
let related_information = defs
.filter_map(|def| {
ModuleFieldGlobal::cast(def.key.to_node(root))
.and_then(|global| global.global_type())
.and_then(|global_type| {
if global_type.mut_keyword().is_none() {
Some(DiagnosticRelatedInformation {
location: Location {
uri: service.lookup_uri(uri),
range: helpers::rowan_range_to_lsp_range(
line_index,
global_type.syntax().text_range(),
),
},
message: "immutable global type".into(),
})
} else {
None
}
})
})
.collect::<Vec<_>>();
if related_information.is_empty() {
None
} else {
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: "mutating an immutable global is not allowed".into(),
related_information: Some(related_information),
..Default::default()
})
}
}));
}
10 changes: 10 additions & 0 deletions crates/service/src/checker/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use lsp_types::Diagnostic;
use wat_syntax::{SyntaxKind, SyntaxNode};

mod dup_names;
mod global_mut;
mod immediates;
mod implicit_module;
mod multi_modules;
Expand Down Expand Up @@ -65,6 +66,15 @@ pub fn check(service: &LanguageService, uri: InternUri) -> Vec<Diagnostic> {
SyntaxKind::PLAIN_INSTR => {
unknown_instr::check(&mut diagnostics, &line_index, &node);
immediates::check(&mut diagnostics, &line_index, &node);
global_mut::check(
service,
&mut diagnostics,
uri,
&line_index,
&root,
&symbol_table,
&node,
);
}
_ => {}
});
Expand Down
72 changes: 72 additions & 0 deletions crates/service/tests/diagnostics/global_mut.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
use super::*;
use insta::assert_json_snapshot;
use lsp_types::Uri;
use wat_service::LanguageService;

#[test]
fn global_get() {
let uri = "untitled:test".parse::<Uri>().unwrap();
let source = "
(module
(global i32
i32.const 0)
(func (result i32)
(global.get 0)))
";
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!(pick_diagnostics(response).is_empty());
}

#[test]
fn undef() {
let uri = "untitled:test".parse::<Uri>().unwrap();
let source = "
(module
(func
(global.set 0)))
";
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);
}

#[test]
fn mutable() {
let uri = "untitled:test".parse::<Uri>().unwrap();
let source = "
(module
(global (mut i32)
i32.const 0)
(func
(global.set 0
(i32.const 0))))
";
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!(pick_diagnostics(response).is_empty());
}

#[test]
fn immutable() {
let uri = "untitled:test".parse::<Uri>().unwrap();
let source = "
(module
(global i32
i32.const 0)
(func
(global.set 0
(i32.const 0))))
";
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);
}
2 changes: 2 additions & 0 deletions crates/service/tests/diagnostics/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ use wat_service::{LanguageService, LintLevel, Lints, ServiceConfig};
#[cfg(test)]
mod dup_names;
#[cfg(test)]
mod global_mut;
#[cfg(test)]
mod immediates;
#[cfg(test)]
mod implicit_module;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
---
source: crates/service/tests/diagnostics/global_mut.rs
expression: response
---
{
"kind": "full",
"items": [
{
"range": {
"start": {
"line": 5,
"character": 16
},
"end": {
"line": 5,
"character": 17
}
},
"severity": 1,
"code": "global-mutation",
"source": "wat",
"message": "mutating an immutable global is not allowed",
"relatedInformation": [
{
"location": {
"uri": "untitled:test",
"range": {
"start": {
"line": 2,
"character": 10
},
"end": {
"line": 2,
"character": 13
}
}
},
"message": "immutable global type"
}
]
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
---
source: crates/service/tests/diagnostics/global_mut.rs
expression: response
---
{
"kind": "full",
"items": [
{
"range": {
"start": {
"line": 3,
"character": 4
},
"end": {
"line": 3,
"character": 18
}
},
"severity": 1,
"code": "type-check",
"source": "wat",
"message": "expected types [any], found []"
},
{
"range": {
"start": {
"line": 3,
"character": 16
},
"end": {
"line": 3,
"character": 17
}
},
"severity": 1,
"code": "undef",
"source": "wat",
"message": "cannot find `0` in this scope"
}
]
}

0 comments on commit f075b77

Please sign in to comment.