about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2021-10-14 11:42:53 +0000
committerGitHub <noreply@github.com>2021-10-14 11:42:53 +0000
commitc5354877c98bf804eaa6148204386dd392eb3b39 (patch)
tree26c1331cadfa19d15490d50f2df32f5487b7b3db
parentf87debcf87c16690e8d5e185a35d03a402a2d5bf (diff)
parent841d4f9dad7edbb9b239dfa6c6a14dcfe5d94ad3 (diff)
downloadrust-c5354877c98bf804eaa6148204386dd392eb3b39.tar.gz
rust-c5354877c98bf804eaa6148204386dd392eb3b39.zip
Merge #10503
10503: Only include targets of packages that are workspace members r=Veykril a=bcully

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

Co-authored-by: Brendan Cully <brendan@cully.org>
-rw-r--r--crates/project_model/src/cargo_workspace.rs10
-rw-r--r--crates/rust-analyzer/tests/slow-tests/main.rs87
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 a55901a8f75..739f29b014d 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 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::<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() {