Unnamed repository; edit this file 'description' to name the repository.
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
| -rw-r--r-- | crates/project_model/src/cargo_workspace.rs | 10 | ||||
| -rw-r--r-- | 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 1417ac2fa1..dccdf1d431 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<Target>, /// 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<PackageDependency>, /// 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<Target> { 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 f92902e2e7..9eb8ad1b72 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::<Runnables>( + 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() { |