diff --git a/crates/project_model/src/cargo_workspace.rs b/crates/project_model/src/cargo_workspace.rs index a55901a8f752..739f29b014df 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 f92902e2e74f..9eb8ad1b720f 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() {