Unnamed repository; edit this file 'description' to name the repository.
internal: Add flycheck test for custom check command and debounce
This adds an integration test for flycheck with a custom check
command.
On its own, the test actually fails due to an issue with the debounce
logic. We would trigger a flycheck when the workspace is loaded, but
the check command references $saved_file and flycheck does nothing (we
don't know which file was saved).
Since we debounce state changes over 50 milliseconds, we would then
discard the later save request that actually specifies the file.
This bug is probably very rare in practice, 50 milliseconds isn't very
long. I think it's a genuine fix though.
AI disclosure: Partly written by Opus 4.6.
| -rw-r--r-- | crates/rust-analyzer/src/flycheck.rs | 34 | ||||
| -rw-r--r-- | crates/rust-analyzer/tests/slow-tests/flycheck.rs | 36 |
2 files changed, 63 insertions, 7 deletions
diff --git a/crates/rust-analyzer/src/flycheck.rs b/crates/rust-analyzer/src/flycheck.rs index f7f861a99e..cdaf944bba 100644 --- a/crates/rust-analyzer/src/flycheck.rs +++ b/crates/rust-analyzer/src/flycheck.rs @@ -559,17 +559,37 @@ impl FlycheckActor { self.cancel_check_process(); } Event::RequestStateChange(StateChange::Restart { - generation, - scope, - saved_file, - target, + mut generation, + mut scope, + mut saved_file, + mut target, }) => { // Cancel the previously spawned process self.cancel_check_process(); + + // Debounce by briefly waiting for other state changes. while let Ok(restart) = inbox.recv_timeout(Duration::from_millis(50)) { - // restart chained with a stop, so just cancel - if let StateChange::Cancel = restart { - continue 'event; + match restart { + StateChange::Cancel => { + // We got a cancel straight after this restart request, so + // don't do anything. + continue 'event; + } + StateChange::Restart { + generation: g, + scope: s, + saved_file: sf, + target: t, + } => { + // We got another restart request. Take the parameters + // from the last restart request in this time window, + // because the most recent request is probably the most + // relevant to the user. + generation = g; + scope = s; + saved_file = sf; + target = t; + } } } diff --git a/crates/rust-analyzer/tests/slow-tests/flycheck.rs b/crates/rust-analyzer/tests/slow-tests/flycheck.rs index 0aa107e823..c1d53fb33a 100644 --- a/crates/rust-analyzer/tests/slow-tests/flycheck.rs +++ b/crates/rust-analyzer/tests/slow-tests/flycheck.rs @@ -74,3 +74,39 @@ fn main() { // Wait for diagnostics to be cleared. server.wait_for_diagnostics_cleared(); } + +#[test] +fn test_flycheck_diagnostic_with_override_command() { + if skip_slow_tests() { + return; + } + + let server = Project::with_fixture( + r#" +//- /Cargo.toml +[package] +name = "foo" +version = "0.0.0" + +//- /src/main.rs +fn main() {} +"#, + ) + .with_config(serde_json::json!({ + "checkOnSave": true, + "check": { + "overrideCommand": ["rustc", "--error-format=json", "$saved_file"] + } + })) + .server() + .wait_until_workspace_is_loaded(); + + server.write_file_and_save("src/main.rs", "fn main() {\n let x = 1;\n}\n".to_owned()); + + let diagnostics = server.wait_for_diagnostics(); + assert!( + diagnostics.diagnostics.iter().any(|d| d.message.contains("unused variable")), + "expected unused variable diagnostic, got: {:?}", + diagnostics.diagnostics, + ); +} |