about summary refs log tree commit diff
diff options
context:
space:
mode:
authorDavid Barsky <me@davidbarsky.com>2023-03-28 09:17:16 -0400
committerDavid Barsky <me@davidbarsky.com>2023-03-28 09:17:16 -0400
commit6a42d7f627958d20587c8a137ed1756465d13b1e (patch)
tree2cb87fc12c1e7777d541496584bb60efeeb2b7d8
parente7337fc9c3b4995fdab4e90871bfb48a26d5eaec (diff)
downloadrust-6a42d7f627958d20587c8a137ed1756465d13b1e.tar.gz
rust-6a42d7f627958d20587c8a137ed1756465d13b1e.zip
fix: allow new, subsequent `rust-project.json`-based workspaces to get
proc macro expansion.
-rw-r--r--crates/project-model/src/workspace.rs8
-rw-r--r--crates/rust-analyzer/src/reload.rs43
2 files changed, 42 insertions, 9 deletions
diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs
index 916447fdffa..5f23d9fe826 100644
--- a/crates/project-model/src/workspace.rs
+++ b/crates/project-model/src/workspace.rs
@@ -672,6 +672,14 @@ impl ProjectWorkspace {
             _ => false,
         }
     }
+
+    /// Returns `true` if the project workspace is [`Json`].
+    ///
+    /// [`Json`]: ProjectWorkspace::Json
+    #[must_use]
+    pub fn is_json(&self) -> bool {
+        matches!(self, Self::Json { .. })
+    }
 }
 
 fn project_json_to_crate_graph(
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs
index 7b27a067062..0c76ac8b925 100644
--- a/crates/rust-analyzer/src/reload.rs
+++ b/crates/rust-analyzer/src/reload.rs
@@ -296,11 +296,25 @@ impl GlobalState {
         let workspaces =
             workspaces.iter().filter_map(|res| res.as_ref().ok().cloned()).collect::<Vec<_>>();
 
-        let same_workspaces = workspaces.len() == self.workspaces.len()
-            && workspaces
-                .iter()
-                .zip(self.workspaces.iter())
-                .all(|(l, r)| l.eq_ignore_build_data(r));
+        // `different_workspaces` is used to spawn a new proc macro server for a newly-added
+        // rust workspace (most commonly sourced from a `rust-project.json`). While the algorithm
+        // to find the new workspaces is quadratic, we generally expect that the number of total
+        // workspaces to remain in the low single digits. the `cloned_workspace` is needed for borrowck
+        // reasons.
+        let cloned_workspaces = workspaces.clone();
+        let different_workspaces = cloned_workspaces
+            .iter()
+            .filter(|ws| {
+                !self
+                    .workspaces
+                    .iter()
+                    .find(|existing_ws| ws.eq_ignore_build_data(&existing_ws))
+                    .is_some()
+            })
+            .collect::<Vec<_>>();
+        let same_workspaces = different_workspaces.is_empty();
+
+        tracing::debug!(current_workspaces = ?self.workspaces, new_workspaces = ?workspaces, ?same_workspaces, "comparing workspaces");
 
         if same_workspaces {
             let (workspaces, build_scripts) = self.fetch_build_data_queue.last_op_result();
@@ -370,11 +384,10 @@ impl GlobalState {
         let files_config = self.config.files();
         let project_folders = ProjectFolders::new(&self.workspaces, &files_config.exclude);
 
-        if self.proc_macro_clients.is_empty() {
+        if self.proc_macro_clients.is_empty() || !different_workspaces.is_empty() {
             if let Some((path, path_manually_set)) = self.config.proc_macro_srv() {
                 tracing::info!("Spawning proc-macro servers");
-                self.proc_macro_clients = self
-                    .workspaces
+                self.proc_macro_clients = different_workspaces
                     .iter()
                     .map(|ws| {
                         let (path, args): (_, &[_]) = if path_manually_set {
@@ -448,7 +461,19 @@ impl GlobalState {
         };
         let mut change = Change::new();
 
-        if same_workspaces {
+        // `self.fetch_proc_macros_queue.request_op(cause, proc_macro_paths)` is only called in
+        // when `switch_workspaces` is called _without_ changing workspaces. This typically occurs
+        // when build scripts have finishing building, but when rust-analyzer is used with a
+        // rust-project.json, the build scripts have already been built by the external build system
+        // that generated the `rust-project.json`.
+
+        // Therefore, in order to allow _new_ workspaces added via rust-project.json (e.g., after
+        // a workspace was already added), we check whether this is the same workspace _or_
+        // if any of the new workspaces is a `rust-project.json`.
+        //
+        // The else branch is used to provide better diagnostics to users while procedural macros
+        // are still being built.
+        if same_workspaces || different_workspaces.iter().any(|ws| ws.is_json()) {
             if self.config.expand_proc_macros() {
                 self.fetch_proc_macros_queue.request_op(cause, proc_macro_paths);
             }