Unnamed repository; edit this file 'description' to name the repository.
flycheck: Make the flycheckable unit a flycheck::PackageSpecifier enum
You should be able to flycheck a ProjectJson crate based on its build label. This paves the way for that. Context: I don't think this has been working for some time. It used to be that we would use cargo to build ProjectJson crates. Support for ProjectJson seems to have been somewhat steamrolled in PR 18845 (e4bf6e1bc36e4cbc8a36d7911788176eb9fac76e).
Cormac Relf 4 months ago
parent a783265 · commit c5c52e8
-rw-r--r--crates/rust-analyzer/src/diagnostics.rs16
-rw-r--r--crates/rust-analyzer/src/flycheck.rs69
-rw-r--r--crates/rust-analyzer/src/global_state.rs52
-rw-r--r--crates/rust-analyzer/src/handlers/notification.rs42
4 files changed, 131 insertions, 48 deletions
diff --git a/crates/rust-analyzer/src/diagnostics.rs b/crates/rust-analyzer/src/diagnostics.rs
index 4a247800af..712960f13d 100644
--- a/crates/rust-analyzer/src/diagnostics.rs
+++ b/crates/rust-analyzer/src/diagnostics.rs
@@ -3,7 +3,6 @@ pub(crate) mod flycheck_to_proto;
use std::mem;
-use cargo_metadata::PackageId;
use ide::FileId;
use ide_db::{FxHashMap, base_db::DbPanicContext};
use itertools::Itertools;
@@ -12,10 +11,13 @@ use smallvec::SmallVec;
use stdx::iter_eq_by;
use triomphe::Arc;
-use crate::{global_state::GlobalStateSnapshot, lsp, lsp_ext, main_loop::DiagnosticsTaskKind};
+use crate::{
+ flycheck::PackageSpecifier, global_state::GlobalStateSnapshot, lsp, lsp_ext,
+ main_loop::DiagnosticsTaskKind,
+};
pub(crate) type CheckFixes =
- Arc<Vec<FxHashMap<Option<Arc<PackageId>>, FxHashMap<FileId, Vec<Fix>>>>>;
+ Arc<Vec<FxHashMap<Option<PackageSpecifier>, FxHashMap<FileId, Vec<Fix>>>>>;
#[derive(Debug, Default, Clone)]
pub struct DiagnosticsMapConfig {
@@ -29,7 +31,7 @@ pub(crate) type DiagnosticsGeneration = usize;
#[derive(Debug, Clone, Default)]
pub(crate) struct WorkspaceFlycheckDiagnostic {
- pub(crate) per_package: FxHashMap<Option<Arc<PackageId>>, PackageFlycheckDiagnostic>,
+ pub(crate) per_package: FxHashMap<Option<PackageSpecifier>, PackageFlycheckDiagnostic>,
}
#[derive(Debug, Clone)]
@@ -85,7 +87,7 @@ impl DiagnosticCollection {
pub(crate) fn clear_check_for_package(
&mut self,
flycheck_id: usize,
- package_id: Arc<PackageId>,
+ package_id: PackageSpecifier,
) {
let Some(check) = self.check.get_mut(flycheck_id) else {
return;
@@ -124,7 +126,7 @@ impl DiagnosticCollection {
pub(crate) fn clear_check_older_than_for_package(
&mut self,
flycheck_id: usize,
- package_id: Arc<PackageId>,
+ package_id: PackageSpecifier,
generation: DiagnosticsGeneration,
) {
let Some(check) = self.check.get_mut(flycheck_id) else {
@@ -154,7 +156,7 @@ impl DiagnosticCollection {
&mut self,
flycheck_id: usize,
generation: DiagnosticsGeneration,
- package_id: &Option<Arc<PackageId>>,
+ package_id: &Option<PackageSpecifier>,
file_id: FileId,
diagnostic: lsp_types::Diagnostic,
fix: Option<Box<Fix>>,
diff --git a/crates/rust-analyzer/src/flycheck.rs b/crates/rust-analyzer/src/flycheck.rs
index b062641691..2819ae98da 100644
--- a/crates/rust-analyzer/src/flycheck.rs
+++ b/crates/rust-analyzer/src/flycheck.rs
@@ -195,9 +195,9 @@ impl FlycheckHandle {
/// Schedule a re-start of the cargo check worker to do a package wide check.
pub(crate) fn restart_for_package(
&self,
- package: Arc<PackageId>,
+ package: PackageSpecifier,
target: Option<Target>,
- workspace_deps: Option<FxHashSet<Arc<PackageId>>>,
+ workspace_deps: Option<FxHashSet<PackageSpecifier>>,
) {
let generation = self.generation.fetch_add(1, Ordering::Relaxed) + 1;
self.sender
@@ -233,7 +233,7 @@ pub(crate) enum ClearDiagnosticsKind {
#[derive(Debug)]
pub(crate) enum ClearScope {
Workspace,
- Package(Arc<PackageId>),
+ Package(PackageSpecifier),
}
pub(crate) enum FlycheckMessage {
@@ -243,7 +243,7 @@ pub(crate) enum FlycheckMessage {
generation: DiagnosticsGeneration,
workspace_root: Arc<AbsPathBuf>,
diagnostic: Diagnostic,
- package_id: Option<Arc<PackageId>>,
+ package_id: Option<PackageSpecifier>,
},
/// Request clearing all outdated diagnostics.
@@ -295,7 +295,32 @@ pub(crate) enum Progress {
enum FlycheckScope {
Workspace,
- Package { package: Arc<PackageId>, workspace_deps: Option<FxHashSet<Arc<PackageId>>> },
+ Package {
+ // Either a cargo package or a $label in rust-project.check.overrideCommand
+ package: PackageSpecifier,
+ workspace_deps: Option<FxHashSet<PackageSpecifier>>,
+ },
+}
+
+#[derive(Debug, Hash, PartialEq, Eq, Clone)]
+pub(crate) enum PackageSpecifier {
+ Cargo {
+ /// The one in Cargo.toml, assumed to work with `cargo check -p {}` etc
+ package_id: Arc<PackageId>,
+ },
+ BuildInfo {
+ /// If a `build` field is present in rust-project.json, its label field
+ label: String,
+ },
+}
+
+impl PackageSpecifier {
+ pub(crate) fn as_str(&self) -> &str {
+ match self {
+ Self::Cargo { package_id } => &package_id.repr,
+ Self::BuildInfo { label } => label,
+ }
+ }
}
enum StateChange {
@@ -331,7 +356,7 @@ struct FlycheckActor {
command_handle: Option<CommandHandle<CargoCheckMessage>>,
/// The receiver side of the channel mentioned above.
command_receiver: Option<Receiver<CargoCheckMessage>>,
- diagnostics_cleared_for: FxHashSet<Arc<PackageId>>,
+ diagnostics_cleared_for: FxHashSet<PackageSpecifier>,
diagnostics_received: DiagnosticsReceived,
}
@@ -564,7 +589,10 @@ impl FlycheckActor {
msg.target.kind.iter().format_with(", ", |kind, f| f(&kind)),
)));
let package_id = Arc::new(msg.package_id);
- if self.diagnostics_cleared_for.insert(package_id.clone()) {
+ if self
+ .diagnostics_cleared_for
+ .insert(PackageSpecifier::Cargo { package_id: package_id.clone() })
+ {
tracing::trace!(
flycheck_id = self.id,
package_id = package_id.repr,
@@ -572,7 +600,9 @@ impl FlycheckActor {
);
self.send(FlycheckMessage::ClearDiagnostics {
id: self.id,
- kind: ClearDiagnosticsKind::All(ClearScope::Package(package_id)),
+ kind: ClearDiagnosticsKind::All(ClearScope::Package(
+ PackageSpecifier::Cargo { package_id },
+ )),
});
}
}
@@ -580,7 +610,7 @@ impl FlycheckActor {
tracing::trace!(
flycheck_id = self.id,
message = diagnostic.message,
- package_id = package_id.as_ref().map(|it| &it.repr),
+ package_id = package_id.as_ref().map(|it| it.as_str()),
"diagnostic received"
);
if self.diagnostics_received == DiagnosticsReceived::No {
@@ -590,7 +620,7 @@ impl FlycheckActor {
if self.diagnostics_cleared_for.insert(package_id.clone()) {
tracing::trace!(
flycheck_id = self.id,
- package_id = package_id.repr,
+ package_id = package_id.as_str(),
"clearing diagnostics"
);
self.send(FlycheckMessage::ClearDiagnostics {
@@ -666,7 +696,18 @@ impl FlycheckActor {
match scope {
FlycheckScope::Workspace => cmd.arg("--workspace"),
- FlycheckScope::Package { package, .. } => cmd.arg("-p").arg(&package.repr),
+ FlycheckScope::Package {
+ package: PackageSpecifier::Cargo { package_id },
+ ..
+ } => cmd.arg("-p").arg(&package_id.repr),
+ FlycheckScope::Package {
+ package: PackageSpecifier::BuildInfo { .. }, ..
+ } => {
+ // No way to flycheck this single package. All we have is a build label.
+ // There's no way to really say whether this build label happens to be
+ // a cargo canonical name, so we won't try.
+ return None;
+ }
};
if let Some(tgt) = target {
@@ -748,7 +789,7 @@ impl FlycheckActor {
#[allow(clippy::large_enum_variant)]
enum CargoCheckMessage {
CompilerArtifact(cargo_metadata::Artifact),
- Diagnostic { diagnostic: Diagnostic, package_id: Option<Arc<PackageId>> },
+ Diagnostic { diagnostic: Diagnostic, package_id: Option<PackageSpecifier> },
}
struct CargoCheckParser;
@@ -767,7 +808,9 @@ impl JsonLinesParser<CargoCheckMessage> for CargoCheckParser {
cargo_metadata::Message::CompilerMessage(msg) => {
Some(CargoCheckMessage::Diagnostic {
diagnostic: msg.message,
- package_id: Some(Arc::new(msg.package_id)),
+ package_id: Some(PackageSpecifier::Cargo {
+ package_id: Arc::new(msg.package_id),
+ }),
})
}
_ => None,
diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs
index 68d65cdee6..0cfd0a141b 100644
--- a/crates/rust-analyzer/src/global_state.rs
+++ b/crates/rust-analyzer/src/global_state.rs
@@ -9,7 +9,6 @@ use std::{
time::{Duration, Instant},
};
-use cargo_metadata::PackageId;
use crossbeam_channel::{Receiver, Sender, unbounded};
use hir::ChangeWithProcMacros;
use ide::{Analysis, AnalysisHost, Cancellable, FileId, SourceRootId};
@@ -36,7 +35,7 @@ use crate::{
config::{Config, ConfigChange, ConfigErrors, RatomlFileKind},
diagnostics::{CheckFixes, DiagnosticCollection},
discover,
- flycheck::{FlycheckHandle, FlycheckMessage},
+ flycheck::{FlycheckHandle, FlycheckMessage, PackageSpecifier},
line_index::{LineEndings, LineIndex},
lsp::{from_proto, to_proto::url_from_abs_path},
lsp_ext,
@@ -845,20 +844,43 @@ impl GlobalStateSnapshot {
pub(crate) fn all_workspace_dependencies_for_package(
&self,
- package: &Arc<PackageId>,
- ) -> Option<FxHashSet<Arc<PackageId>>> {
- self.workspaces.iter().find_map(|workspace| match &workspace.kind {
- ProjectWorkspaceKind::Cargo { cargo, .. }
- | ProjectWorkspaceKind::DetachedFile { cargo: Some((cargo, _, _)), .. } => {
- let package = cargo.packages().find(|p| cargo[*p].id == *package)?;
-
- return cargo[package]
- .all_member_deps
- .as_ref()
- .map(|deps| deps.iter().map(|dep| cargo[*dep].id.clone()).collect());
+ package: &PackageSpecifier,
+ ) -> Option<FxHashSet<PackageSpecifier>> {
+ match package {
+ PackageSpecifier::Cargo { package_id } => {
+ self.workspaces.iter().find_map(|workspace| match &workspace.kind {
+ ProjectWorkspaceKind::Cargo { cargo, .. }
+ | ProjectWorkspaceKind::DetachedFile { cargo: Some((cargo, _, _)), .. } => {
+ let package = cargo.packages().find(|p| cargo[*p].id == *package_id)?;
+
+ cargo[package].all_member_deps.as_ref().map(|deps| {
+ deps.iter()
+ .map(|dep| cargo[*dep].id.clone())
+ .map(|p| PackageSpecifier::Cargo { package_id: p })
+ .collect()
+ })
+ }
+ _ => None,
+ })
}
- _ => None,
- })
+ PackageSpecifier::BuildInfo { label } => {
+ self.workspaces.iter().find_map(|workspace| match &workspace.kind {
+ ProjectWorkspaceKind::Json(p) => {
+ let krate = p.crate_by_label(label)?;
+ Some(
+ krate
+ .iter_deps()
+ .filter_map(|dep| p[dep].build.as_ref())
+ .map(|build| PackageSpecifier::BuildInfo {
+ label: build.label.clone(),
+ })
+ .collect(),
+ )
+ }
+ _ => None,
+ })
+ }
+ }
}
pub(crate) fn file_exists(&self, file_id: FileId) -> bool {
diff --git a/crates/rust-analyzer/src/handlers/notification.rs b/crates/rust-analyzer/src/handlers/notification.rs
index 4a6544508f..57adbbfe72 100644
--- a/crates/rust-analyzer/src/handlers/notification.rs
+++ b/crates/rust-analyzer/src/handlers/notification.rs
@@ -18,7 +18,7 @@ use vfs::{AbsPathBuf, ChangeKind, VfsPath};
use crate::{
config::{Config, ConfigChange},
- flycheck::{InvocationStrategy, Target},
+ flycheck::{InvocationStrategy, PackageSpecifier, Target},
global_state::{FetchWorkspaceRequest, GlobalState},
lsp::{from_proto, utils::apply_document_changes},
lsp_ext::{self, RunFlycheckParams},
@@ -328,22 +328,32 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool {
}
InvocationStrategy::PerWorkspace => {
Box::new(move || {
- let target = TargetSpec::for_file(&world, file_id)?.and_then(|it| {
+ let target = TargetSpec::for_file(&world, file_id)?.map(|it| {
let tgt_kind = it.target_kind();
let (tgt_name, root, package) = match it {
- TargetSpec::Cargo(c) => (c.target, c.workspace_root, c.package_id),
- _ => return None,
+ TargetSpec::Cargo(c) => (
+ Some(c.target),
+ c.workspace_root,
+ PackageSpecifier::Cargo { package_id: c.package_id },
+ ),
+ TargetSpec::ProjectJson(p) => (
+ None,
+ p.project_root,
+ PackageSpecifier::BuildInfo { label: p.label.clone() },
+ ),
};
- let tgt = match tgt_kind {
- project_model::TargetKind::Bin => Target::Bin(tgt_name),
- project_model::TargetKind::Example => Target::Example(tgt_name),
- project_model::TargetKind::Test => Target::Test(tgt_name),
- project_model::TargetKind::Bench => Target::Benchmark(tgt_name),
- _ => return Some((None, root, package)),
- };
+ let tgt = tgt_name.and_then(|tgt_name| {
+ Some(match tgt_kind {
+ project_model::TargetKind::Bin => Target::Bin(tgt_name),
+ project_model::TargetKind::Example => Target::Example(tgt_name),
+ project_model::TargetKind::Test => Target::Test(tgt_name),
+ project_model::TargetKind::Bench => Target::Benchmark(tgt_name),
+ _ => return None,
+ })
+ });
- Some((Some(tgt), root, package))
+ (tgt, root, package)
});
tracing::debug!(?target, "flycheck target");
// we have a specific non-library target, attempt to only check that target, nothing
@@ -365,7 +375,13 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool {
cargo: Some((cargo, _, _)),
..
} => *cargo.workspace_root() == root,
- _ => false,
+ project_model::ProjectWorkspaceKind::Json(p) => {
+ *p.project_root() == root
+ }
+ project_model::ProjectWorkspaceKind::DetachedFile {
+ cargo: None,
+ ..
+ } => false,
});
if let Some(idx) = package_workspace_idx {
let workspace_deps =