From 841d4f9dad7edbb9b239dfa6c6a14dcfe5d94ad3 Mon Sep 17 00:00:00 2001 From: Brendan Cully Date: Sat, 9 Oct 2021 19:49:34 -0700 Subject: [PATCH] Only include targets of packages that are workspace members CargoWorkspace's package list includes packages that are path dependencies, even if those packages aren't actually members of the cargo workspace. As a result, rust-analyzer's runnable finder, which returns the target from the first workspace that has a matching package, may select the wrong working directory, causing runnables to fail, e.g., ``` error: package `root` cannot be tested because it requires dev-dependencies and is not a member of the workspace ``` To fix this, we filter out packages that aren't members of the workspace when searching for targets. Fixes #7764 --- crates/project_model/src/cargo_workspace.rs | 10 ++- crates/rust-analyzer/tests/slow-tests/main.rs | 87 +++++++++++++++++++ 2 files changed, 95 insertions(+), 2 deletions(-) diff --git a/crates/project_model/src/cargo_workspace.rs b/crates/project_model/src/cargo_workspace.rs index 1417ac2fa1d..dccdf1d431a 100644 --- a/crates/project_model/src/cargo_workspace.rs +++ b/crates/project_model/src/cargo_workspace.rs @@ -137,6 +137,8 @@ pub struct PackageData { pub targets: Vec, /// Does this package come from the local filesystem (and is editable)? pub is_local: bool, + // Whether this package is a member of the workspace + pub is_member: bool, /// List of packages this package depends on pub dependencies: Vec, /// Rust edition for this package @@ -296,6 +298,8 @@ impl CargoWorkspace { let mut packages = Arena::default(); let mut targets = Arena::default(); + let ws_members = &meta.workspace_members; + meta.packages.sort_by(|a, b| a.id.cmp(&b.id)); for meta_pkg in &meta.packages { let cargo_metadata::Package { @@ -309,6 +313,7 @@ impl CargoWorkspace { // We treat packages without source as "local" packages. That includes all members of // the current workspace, as well as any path dependency outside the workspace. let is_local = meta_pkg.source.is_none(); + let is_member = ws_members.contains(id); let pkg = packages.alloc(PackageData { id: id.repr.clone(), @@ -317,6 +322,7 @@ impl CargoWorkspace { manifest: AbsPathBuf::assert(PathBuf::from(&manifest_path)).try_into().unwrap(), targets: Vec::new(), is_local, + is_member, edition, dependencies: Vec::new(), features: meta_pkg.features.clone().into_iter().collect(), @@ -383,8 +389,8 @@ impl CargoWorkspace { pub fn target_by_root(&self, root: &AbsPath) -> Option { self.packages() - .filter_map(|pkg| self[pkg].targets.iter().find(|&&it| &self[it].root == root)) - .next() + .filter(|&pkg| self[pkg].is_member) + .find_map(|pkg| self[pkg].targets.iter().find(|&&it| &self[it].root == root)) .copied() } diff --git a/crates/rust-analyzer/tests/slow-tests/main.rs b/crates/rust-analyzer/tests/slow-tests/main.rs index f92902e2e74..9eb8ad1b720 100644 --- a/crates/rust-analyzer/tests/slow-tests/main.rs +++ b/crates/rust-analyzer/tests/slow-tests/main.rs @@ -202,6 +202,93 @@ fn main() {} ); } +// Each package in these workspaces should be run from its own root +#[test] +fn test_path_dependency_runnables() { + if skip_slow_tests() { + return; + } + + let server = Project::with_fixture( + r#" +//- /consumer/Cargo.toml +[package] +name = "consumer" +version = "0.1.0" +[dependencies] +dependency = { path = "../dependency" } + +//- /consumer/src/lib.rs +#[cfg(test)] +mod tests { + #[test] + fn consumer() {} +} + +//- /dependency/Cargo.toml +[package] +name = "dependency" +version = "0.1.0" +[dev-dependencies] +devdependency = { path = "../devdependency" } + +//- /dependency/src/lib.rs +#[cfg(test)] +mod tests { + #[test] + fn dependency() {} +} + +//- /devdependency/Cargo.toml +[package] +name = "devdependency" +version = "0.1.0" + +//- /devdependency/src/lib.rs +#[cfg(test)] +mod tests { + #[test] + fn devdependency() {} +} + "#, + ) + .root("consumer") + .root("dependency") + .root("devdependency") + .server() + .wait_until_workspace_is_loaded(); + + for runnable in ["consumer", "dependency", "devdependency"] { + server.request::( + RunnablesParams { + text_document: server.doc_id(&format!("{}/src/lib.rs", runnable)), + position: None, + }, + json!([ + "{...}", + { + "label": "cargo test -p [..] --all-targets", + "kind": "cargo", + "args": { + "overrideCargo": null, + "workspaceRoot": server.path().join(runnable), + "cargoArgs": [ + "test", + "--package", + runnable, + "--all-targets" + ], + "cargoExtraArgs": [], + "executableArgs": [] + }, + }, + "{...}", + "{...}" + ]), + ); + } +} + #[test] fn test_format_document() { if skip_slow_tests() {