Repository: rust-analyzer/rust-analyzer
Pull request #7236 (2021-01-10 15:09:05)

Fix progress token is already registered crash
bors r+
🤖


Patch:
diff --git a/crates/parser/src/grammar/type_args.rs b/crates/parser/src/grammar/type_args.rs
index debb23fea1..a013c49b9a 100644
--- a/crates/parser/src/grammar/type_args.rs
+++ b/crates/parser/src/grammar/type_args.rs
@@ -26,7 +26,7 @@ pub(super) fn opt_generic_arg_list(p: &mut Parser, colon_colon_required: bool) {
 }
 
 // test type_arg
-// type A = B<'static, i32, 1, { 2 }, Item=u64, true, false>;
+// type A = B<'static, i32, 1, { 2 }, Item=u64>;
 fn generic_arg(p: &mut Parser) {
     let m = p.start();
     match p.current() {
@@ -55,10 +55,6 @@ fn generic_arg(p: &mut Parser) {
             expressions::literal(p);
             m.complete(p, CONST_ARG);
         }
-        TRUE_KW | FALSE_KW => {
-            expressions::literal(p);
-            m.complete(p, CONST_ARG);
-        }
         _ => {
             types::type_(p);
             m.complete(p, TYPE_ARG);
diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs
index 1f6bf1c8cb..442fbd14c6 100644
--- a/crates/rust-analyzer/src/global_state.rs
+++ b/crates/rust-analyzer/src/global_state.rs
@@ -22,6 +22,7 @@ use crate::{
     from_proto,
     line_endings::LineEndings,
     main_loop::Task,
+    op_queue::OpQueue,
     reload::SourceRootConfig,
     request_metrics::{LatestRequests, RequestMetrics},
     thread_pool::TaskPool,
@@ -78,6 +79,7 @@ pub(crate) struct GlobalState {
     pub(crate) source_root_config: SourceRootConfig,
     pub(crate) proc_macro_client: Option<ProcMacroClient>,
     pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>,
+    pub(crate) fetch_workspaces_queue: OpQueue,
     latest_requests: Arc<RwLock<LatestRequests>>,
 }
 
@@ -130,6 +132,7 @@ impl GlobalState {
             source_root_config: SourceRootConfig::default(),
             proc_macro_client: None,
             workspaces: Arc::new(Vec::new()),
+            fetch_workspaces_queue: OpQueue::default(),
             latest_requests: Default::default(),
         }
     }
diff --git a/crates/rust-analyzer/src/lib.rs b/crates/rust-analyzer/src/lib.rs
index c9494e3005..2207b9a875 100644
--- a/crates/rust-analyzer/src/lib.rs
+++ b/crates/rust-analyzer/src/lib.rs
@@ -35,6 +35,7 @@ mod lsp_utils;
 mod thread_pool;
 mod document;
 mod diff;
+mod op_queue;
 pub mod lsp_ext;
 pub mod config;
 
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index 22ee96775c..51fb2eb746 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -11,7 +11,6 @@ use ide::{Canceled, FileId};
 use ide_db::base_db::VfsPath;
 use lsp_server::{Connection, Notification, Request, Response};
 use lsp_types::notification::Notification as _;
-use project_model::ProjectWorkspace;
 use vfs::ChangeKind;
 
 use crate::{
@@ -62,7 +61,6 @@ enum Event {
 pub(crate) enum Task {
     Response(Response),
     Diagnostics(Vec<(FileId, Vec<lsp_types::Diagnostic>)>),
-    Workspaces(Vec<anyhow::Result<ProjectWorkspace>>),
     PrimeCaches(PrimeCachesProgress),
     FetchWorkspace(ProjectWorkspaceProgress),
 }
@@ -143,7 +141,8 @@ impl GlobalState {
             |_, _| (),
         );
 
-        self.fetch_workspaces();
+        self.fetch_workspaces_request();
+        self.fetch_workspaces_if_needed();
 
         while let Some(event) = self.next_event(&inbox) {
             if let Event::Lsp(lsp_server::Message::Notification(not)) = &event {
@@ -204,7 +203,6 @@ impl GlobalState {
                                 self.diagnostics.set_native_diagnostics(file_id, diagnostics)
                             }
                         }
-                        Task::Workspaces(workspaces) => self.switch_workspaces(workspaces),
                         Task::PrimeCaches(progress) => match progress {
                             PrimeCachesProgress::Started => prime_caches_progress.push(progress),
                             PrimeCachesProgress::StartedOnCrate { .. } => {
@@ -224,7 +222,11 @@ impl GlobalState {
                                 ProjectWorkspaceProgress::Report(msg) => {
                                     (Progress::Report, Some(msg))
                                 }
-                                ProjectWorkspaceProgress::End => (Progress::End, None),
+                                ProjectWorkspaceProgress::End(workspaces) => {
+                                    self.fetch_workspaces_completed();
+                                    self.switch_workspaces(workspaces);
+                                    (Progress::End, None)
+                                }
                             };
                             self.report_progress("fetching", state, msg, None);
                         }
@@ -403,6 +405,8 @@ impl GlobalState {
             }
         }
 
+        self.fetch_workspaces_if_needed();
+
         let loop_duration = loop_start.elapsed();
         if loop_duration > Duration::from_millis(100) {
             log::warn!("overly long loop turn: {:?}", loop_duration);
@@ -440,7 +444,7 @@ impl GlobalState {
         }
 
         RequestDispatcher { req: Some(req), global_state: self }
-            .on_sync::<lsp_ext::ReloadWorkspace>(|s, ()| Ok(s.fetch_workspaces()))?
+            .on_sync::<lsp_ext::ReloadWorkspace>(|s, ()| Ok(s.fetch_workspaces_request()))?
             .on_sync::<lsp_ext::JoinLines>(|s, p| handlers::handle_join_lines(s.snapshot(), p))?
             .on_sync::<lsp_ext::OnEnter>(|s, p| handlers::handle_on_enter(s.snapshot(), p))?
             .on_sync::<lsp_types::request::Shutdown>(|s, ()| {
diff --git a/crates/rust-analyzer/src/op_queue.rs b/crates/rust-analyzer/src/op_queue.rs
new file mode 100644
index 0000000000..51d66f4b37
--- /dev/null
+++ b/crates/rust-analyzer/src/op_queue.rs
@@ -0,0 +1,25 @@
+//! Bookkeeping to make sure only one long-running operation is executed.
+
+#[derive(Default)]
+pub(crate) struct OpQueue {
+    op_scheduled: bool,
+    op_in_progress: bool,
+}
+
+impl OpQueue {
+    pub(crate) fn request_op(&mut self) {
+        self.op_scheduled = true;
+    }
+    pub(crate) fn should_start_op(&mut self) -> bool {
+        if !self.op_in_progress && self.op_scheduled {
+            self.op_in_progress = true;
+            self.op_scheduled = false;
+            return true;
+        }
+        false
+    }
+    pub(crate) fn op_completed(&mut self) {
+        assert!(self.op_in_progress);
+        self.op_in_progress = false;
+    }
+}
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs
index f4e084741b..accf2ef8c4 100644
--- a/crates/rust-analyzer/src/reload.rs
+++ b/crates/rust-analyzer/src/reload.rs
@@ -19,7 +19,7 @@ use lsp_ext::StatusParams;
 pub(crate) enum ProjectWorkspaceProgress {
     Begin,
     Report(String),
-    End,
+    End(Vec<anyhow::Result<ProjectWorkspace>>),
 }
 
 impl GlobalState {
@@ -30,7 +30,7 @@ impl GlobalState {
             self.analysis_host.update_lru_capacity(self.config.lru_capacity());
         }
         if self.config.linked_projects() != old_config.linked_projects() {
-            self.fetch_workspaces()
+            self.fetch_workspaces_request()
         } else if self.config.flycheck() != old_config.flycheck() {
             self.reload_flycheck();
         }
@@ -44,7 +44,7 @@ impl GlobalState {
             Status::Ready | Status::Invalid => (),
         }
         if self.config.cargo_autoreload() {
-            self.fetch_workspaces();
+            self.fetch_workspaces_request();
         } else {
             self.transition(Status::NeedsReload);
         }
@@ -98,8 +98,15 @@ impl GlobalState {
             });
         }
     }
-    pub(crate) fn fetch_workspaces(&mut self) {
+
+    pub(crate) fn fetch_workspaces_request(&mut self) {
+        self.fetch_workspaces_queue.request_op()
+    }
+    pub(crate) fn fetch_workspaces_if_needed(&mut self) {
         log::info!("will fetch workspaces");
+        if !self.fetch_workspaces_queue.should_start_op() {
+            return;
+        }
 
         self.task_pool.handle.spawn_with_sender({
             let linked_projects = self.config.linked_projects();
@@ -133,12 +140,17 @@ impl GlobalState {
                     })
                     .collect::<Vec<_>>();
 
-                sender.send(Task::FetchWorkspace(ProjectWorkspaceProgress::End)).unwrap();
                 log::info!("did fetch workspaces {:?}", workspaces);
-                sender.send(Task::Workspaces(workspaces)).unwrap()
+                sender
+                    .send(Task::FetchWorkspace(ProjectWorkspaceProgress::End(workspaces)))
+                    .unwrap();
             }
         });
     }
+    pub(crate) fn fetch_workspaces_completed(&mut self) {
+        self.fetch_workspaces_queue.op_completed()
+    }
+
     pub(crate) fn switch_workspaces(&mut self, workspaces: Vec<anyhow::Result<ProjectWorkspace>>) {
         let _p = profile::span("GlobalState::switch_workspaces");
         log::info!("will switch workspaces: {:?}", workspaces);


Test patch:
diff --git a/crates/syntax/test_data/parser/inline/ok/0039_type_arg.rast b/crates/syntax/test_data/parser/inline/ok/0039_type_arg.rast
index 68c0f1c668..51e881a8e2 100644
--- a/crates/syntax/test_data/parser/inline/ok/0039_type_arg.rast
+++ b/crates/syntax/test_data/parser/inline/ok/0039_type_arg.rast
@@ -1,5 +1,5 @@
-SOURCE_FILE@0..59
-  TYPE_ALIAS@0..58
+SOURCE_FILE@0..46
+  TYPE_ALIAS@0..45
     TYPE_KW@0..4 "type"
     WHITESPACE@4..5 " "
     NAME@5..6
@@ -7,12 +7,12 @@ SOURCE_FILE@0..59
     WHITESPACE@6..7 " "
     EQ@7..8 "="
     WHITESPACE@8..9 " "
-    PATH_TYPE@9..57
-      PATH@9..57
-        PATH_SEGMENT@9..57
+    PATH_TYPE@9..44
+      PATH@9..44
+        PATH_SEGMENT@9..44
           NAME_REF@9..10
             IDENT@9..10 "B"
-          GENERIC_ARG_LIST@10..57
+          GENERIC_ARG_LIST@10..44
             L_ANGLE@10..11 "<"
             LIFETIME_ARG@11..18
               LIFETIME@11..18
@@ -51,16 +51,6 @@ SOURCE_FILE@0..59
                   PATH_SEGMENT@40..43
                     NAME_REF@40..43
                       IDENT@40..43 "u64"
-            COMMA@43..44 ","
-            WHITESPACE@44..45 " "
-            CONST_ARG@45..49
-              LITERAL@45..49
-                TRUE_KW@45..49 "true"
-            COMMA@49..50 ","
-            WHITESPACE@50..51 " "
-            CONST_ARG@51..56
-              LITERAL@51..56
-                FALSE_KW@51..56 "false"
-            R_ANGLE@56..57 ">"
-    SEMICOLON@57..58 ";"
-  WHITESPACE@58..59 "\n"
+            R_ANGLE@43..44 ">"
+    SEMICOLON@44..45 ";"
+  WHITESPACE@45..46 "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0039_type_arg.rs b/crates/syntax/test_data/parser/inline/ok/0039_type_arg.rs
index 6a8721a73e..0d07d76514 100644
--- a/crates/syntax/test_data/parser/inline/ok/0039_type_arg.rs
+++ b/crates/syntax/test_data/parser/inline/ok/0039_type_arg.rs
@@ -1,1 +1,1 @@
-type A = B<'static, i32, 1, { 2 }, Item=u64, true, false>;
+type A = B<'static, i32, 1, { 2 }, Item=u64>;