Skip to content

Commit 1ec356c

Browse files
authored
Merge pull request #21738 from Wilfred/custom_check_command_stale_diagnostics
fix: Stale diagnostics when a custom check command is configured
2 parents f7da193 + c6c2135 commit 1ec356c

2 files changed

Lines changed: 67 additions & 21 deletions

File tree

crates/rust-analyzer/src/flycheck.rs

Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -673,27 +673,31 @@ impl FlycheckActor {
673673
if self.diagnostics_received == DiagnosticsReceived::NotYet {
674674
tracing::trace!(flycheck_id = self.id, "clearing diagnostics");
675675
// We finished without receiving any diagnostics.
676-
// Clear everything for good measure
677-
match &self.scope {
678-
FlycheckScope::Workspace => {
679-
self.send(FlycheckMessage::ClearDiagnostics {
680-
id: self.id,
681-
kind: ClearDiagnosticsKind::All(ClearScope::Workspace),
682-
});
683-
}
684-
FlycheckScope::Package { package, workspace_deps } => {
685-
for pkg in
686-
std::iter::once(package).chain(workspace_deps.iter().flatten())
687-
{
688-
self.send(FlycheckMessage::ClearDiagnostics {
689-
id: self.id,
690-
kind: ClearDiagnosticsKind::All(ClearScope::Package(
691-
pkg.clone(),
692-
)),
693-
});
694-
}
695-
}
696-
}
676+
//
677+
// `cargo check` generally outputs something, even if there are no
678+
// warnings/errors, so we always know which package was checked.
679+
//
680+
// ```text
681+
// $ cargo check --message-format=json 2>/dev/null
682+
// {"reason":"compiler-artifact","package_id":"path+file:///Users/wilfred/tmp/scratch#0.1.0",...}
683+
// ```
684+
//
685+
// However, rustc only returns JSON if there are diagnostics present, so a
686+
// build without warnings or errors has an empty output.
687+
//
688+
// ```
689+
// $ rustc --error-format=json bad.rs
690+
// {"$message_type":"diagnostic","message":"mismatched types","...}
691+
//
692+
// $ rustc --error-format=json good.rs
693+
// ```
694+
//
695+
// So if we got zero diagnostics, it was almost certainly a check that
696+
// wasn't specific to a package.
697+
self.send(FlycheckMessage::ClearDiagnostics {
698+
id: self.id,
699+
kind: ClearDiagnosticsKind::All(ClearScope::Workspace),
700+
});
697701
} else if res.is_ok() {
698702
// We clear diagnostics for packages on
699703
// `[CargoCheckMessage::CompilerArtifact]` but there seem to be setups where

crates/rust-analyzer/tests/slow-tests/flycheck.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,3 +110,45 @@ fn main() {}
110110
diagnostics.diagnostics,
111111
);
112112
}
113+
114+
#[test]
115+
fn test_flycheck_diagnostics_with_override_command_cleared_after_fix() {
116+
if skip_slow_tests() {
117+
return;
118+
}
119+
120+
// Start with a program that is lint clean.
121+
let server = Project::with_fixture(
122+
r#"
123+
//- /Cargo.toml
124+
[package]
125+
name = "foo"
126+
version = "0.0.0"
127+
128+
//- /src/main.rs
129+
fn main() {}
130+
"#,
131+
)
132+
.with_config(serde_json::json!({
133+
"checkOnSave": true,
134+
"check": {
135+
"overrideCommand": ["rustc", "--error-format=json", "$saved_file"]
136+
}
137+
}))
138+
.server()
139+
.wait_until_workspace_is_loaded();
140+
141+
// Introduce an unused variable.
142+
server.write_file_and_save("src/main.rs", "fn main() {\n let x = 1;\n}\n".to_owned());
143+
144+
let diags = server.wait_for_diagnostics();
145+
assert!(
146+
diags.diagnostics.iter().any(|d| d.message.contains("unused variable")),
147+
"expected unused variable diagnostic, got: {:?}",
148+
diags.diagnostics,
149+
);
150+
151+
// Fix it and verify that diagnostics are cleared.
152+
server.write_file_and_save("src/main.rs", "fn main() {\n let _x = 1;\n}\n".to_owned());
153+
server.wait_for_diagnostics_cleared();
154+
}

0 commit comments

Comments
 (0)