Unnamed repository; edit this file 'description' to name the repository.
fix: Stale diagnostics with rust-project.json and rustc JSON
PR #18043 changed flycheck to be scoped to the relevant package. This
broke projects using check commands that invoke rustc directly,
because diagnostic JSON from rustc doesn't contain the package ID.
This was visible in the rust-analyzer logs when RA_LOG is set to
`rust_analyzer::flycheck=trace`.
Before:
2026-02-02T07:03:48.020184937-08:00 TRACE diagnostic received flycheck_id=0 mismatched types package_id=None scope=Workspace
...
2026-02-02T07:03:55.082046488-08:00 TRACE clearing diagnostics flycheck_id=0 scope=Workspace
After:
2026-02-02T07:14:32.760707785-08:00 TRACE diagnostic received flycheck_id=0 mismatched types package_id=None scope=Package { package: BuildInfo { label: "fbcode//rust_devx/rust-guess-deps:rust-guess-deps" }, workspace_deps: Some({}) }
...
2026-02-02T07:14:48.355981415-08:00 TRACE clearing diagnostics flycheck_id=0 scope=Package { package: BuildInfo { label: "fbcode//rust_devx/rust-guess-deps:rust-guess-deps" }, workspace_deps: Some({}) }
Previously r-a assumed that a diagnostic without a package ID applied
to the whole workspace. We would insert the diagnostic at the
workspace level, but then only clear diagnostics for the package.
As a result, red squiggles would get 'stuck'. Users who had fixed
compilation issues would still see the old red squiggles until they
introduced a new compilation error.
Instead, always apply diagnostics to the current package if flycheck
is scoped to a package and the diagnostic doesn't specify a
package. This makes CargoCheckEvent(None) and CargoCheckEvent(Some(_))
more consistent, as they now both match on scope.
| -rw-r--r-- | crates/rust-analyzer/src/flycheck.rs | 88 |
1 files changed, 58 insertions, 30 deletions
diff --git a/crates/rust-analyzer/src/flycheck.rs b/crates/rust-analyzer/src/flycheck.rs index 2f38901ce1..616025ac19 100644 --- a/crates/rust-analyzer/src/flycheck.rs +++ b/crates/rust-analyzer/src/flycheck.rs @@ -740,42 +740,70 @@ impl FlycheckActor { flycheck_id = self.id, message = diagnostic.message, package_id = package_id.as_ref().map(|it| it.as_str()), + scope = ?self.scope, "diagnostic received" ); - if self.diagnostics_received == DiagnosticsReceived::NotYet { - self.diagnostics_received = DiagnosticsReceived::AtLeastOne; - } - if let Some(package_id) = &package_id { - if self.diagnostics_cleared_for.insert(package_id.clone()) { - tracing::trace!( - flycheck_id = self.id, - package_id = package_id.as_str(), - "clearing diagnostics" - ); - self.send(FlycheckMessage::ClearDiagnostics { + + match &self.scope { + FlycheckScope::Workspace => { + if self.diagnostics_received == DiagnosticsReceived::NotYet { + self.send(FlycheckMessage::ClearDiagnostics { + id: self.id, + kind: ClearDiagnosticsKind::All(ClearScope::Workspace), + }); + + self.diagnostics_received = + DiagnosticsReceived::AtLeastOneAndClearedWorkspace; + } + + if let Some(package_id) = package_id { + tracing::warn!( + "Ignoring package label {:?} and applying diagnostics to the whole workspace", + package_id + ); + } + + self.send(FlycheckMessage::AddDiagnostic { id: self.id, - kind: ClearDiagnosticsKind::All(ClearScope::Package( - package_id.clone(), - )), + generation: self.generation, + package_id: None, + workspace_root: self.root.clone(), + diagnostic, + }); + } + FlycheckScope::Package { package: flycheck_package, .. } => { + if self.diagnostics_received == DiagnosticsReceived::NotYet { + self.diagnostics_received = DiagnosticsReceived::AtLeastOne; + } + + // If the package has been set in the diagnostic JSON, respect that. Otherwise, use the + // package that the current flycheck is scoped to. This is useful when a project is + // directly using rustc for its checks (e.g. custom check commands in rust-project.json). + let package_id = package_id.unwrap_or(flycheck_package.clone()); + + if self.diagnostics_cleared_for.insert(package_id.clone()) { + tracing::trace!( + flycheck_id = self.id, + package_id = package_id.as_str(), + "clearing diagnostics" + ); + self.send(FlycheckMessage::ClearDiagnostics { + id: self.id, + kind: ClearDiagnosticsKind::All(ClearScope::Package( + package_id.clone(), + )), + }); + } + + self.send(FlycheckMessage::AddDiagnostic { + id: self.id, + generation: self.generation, + package_id: Some(package_id), + workspace_root: self.root.clone(), + diagnostic, }); } - } else if self.diagnostics_received - != DiagnosticsReceived::AtLeastOneAndClearedWorkspace - { - self.diagnostics_received = - DiagnosticsReceived::AtLeastOneAndClearedWorkspace; - self.send(FlycheckMessage::ClearDiagnostics { - id: self.id, - kind: ClearDiagnosticsKind::All(ClearScope::Workspace), - }); } - self.send(FlycheckMessage::AddDiagnostic { - id: self.id, - generation: self.generation, - package_id, - workspace_root: self.root.clone(), - diagnostic, - }); } }, } |