Repository: rust-lang/rust-analyzer
Pull request #12451 (2022-06-02 19:49:33)

feat: Support `$$` in macros.
The implementation mirrors what `rustc` currently does [1]. Part of #11952.

\[1]: https://github.com/rust-lang/rust/blob/0595ea1d12cf745e0a672d05341429ecb0917e66/compiler/rustc_expand/src/mbe/quoted.rs#L230-L241


Patch:
diff --git a/crates/ide-assists/src/handlers/extract_module.rs b/crates/ide-assists/src/handlers/extract_module.rs
index 11349b45d3..b5ed5699c9 100644
--- a/crates/ide-assists/src/handlers/extract_module.rs
+++ b/crates/ide-assists/src/handlers/extract_module.rs
@@ -413,10 +413,7 @@ impl Module {
                                         ctx,
                                     )
                                 {
-                                    check_intersection_and_push(
-                                        &mut import_paths_to_be_removed,
-                                        import_path,
-                                    );
+                                    import_paths_to_be_removed.push(import_path);
                                 }
                             }
                         }
@@ -442,10 +439,7 @@ impl Module {
                                         ctx,
                                     )
                                 {
-                                    check_intersection_and_push(
-                                        &mut import_paths_to_be_removed,
-                                        import_path,
-                                    );
+                                    import_paths_to_be_removed.push(import_path);
                                 }
                             }
                         }
@@ -549,25 +543,12 @@ impl Module {
         } else if exists_inside_sel && !exists_outside_sel {
             //Changes to be made inside new module, and remove import from outside
 
-            if let Some((mut use_tree_str, text_range_opt)) =
+            if let Some((use_tree_str, text_range_opt)) =
                 self.process_use_stmt_for_import_resolve(use_stmt_opt, node_syntax)
             {
                 if let Some(text_range) = text_range_opt {
                     import_path_to_be_removed = Some(text_range);
                 }
-
-                if source_exists_outside_sel_in_same_mod {
-                    if let Some(first_path_in_use_tree) = use_tree_str.last() {
-                        let first_path_in_use_tree_str = first_path_in_use_tree.to_string();
-                        if !first_path_in_use_tree_str.contains("super")
-                            && !first_path_in_use_tree_str.contains("crate")
-                        {
-                            let super_path = make::ext::ident_path("super");
-                            use_tree_str.push(super_path);
-                        }
-                    }
-                }
-
                 use_tree_str_opt = Some(use_tree_str);
             } else if source_exists_outside_sel_in_same_mod {
                 self.make_use_stmt_of_node_with_super(node_syntax);
@@ -577,16 +558,9 @@ impl Module {
         if let Some(use_tree_str) = use_tree_str_opt {
             let mut use_tree_str = use_tree_str;
             use_tree_str.reverse();
-
-            if !(!exists_outside_sel && exists_inside_sel && source_exists_outside_sel_in_same_mod)
-            {
-                if let Some(first_path_in_use_tree) = use_tree_str.first() {
-                    let first_path_in_use_tree_str = first_path_in_use_tree.to_string();
-                    if first_path_in_use_tree_str.contains("super") {
-                        let super_path = make::ext::ident_path("super");
-                        use_tree_str.insert(0, super_path)
-                    }
-                }
+            if use_tree_str[0].to_string().contains("super") {
+                let super_path = make::ext::ident_path("super");
+                use_tree_str.insert(0, super_path)
             }
 
             let use_ =
@@ -647,32 +621,6 @@ impl Module {
     }
 }
 
-fn check_intersection_and_push(
-    import_paths_to_be_removed: &mut Vec<TextRange>,
-    import_path: TextRange,
-) {
-    if import_paths_to_be_removed.len() > 0 {
-        // Text ranges recieved here for imports are extended to the
-        // next/previous comma which can cause intersections among them
-        // and later deletion of these can cause panics similar
-        // to reported in #11766. So to mitigate it, we
-        // check for intersection between all current members
-        // and if it exists we combine both text ranges into
-        // one
-        let r = import_paths_to_be_removed
-            .into_iter()
-            .position(|it| it.intersect(import_path).is_some());
-        match r {
-            Some(it) => {
-                import_paths_to_be_removed[it] = import_paths_to_be_removed[it].cover(import_path)
-            }
-            None => import_paths_to_be_removed.push(import_path),
-        }
-    } else {
-        import_paths_to_be_removed.push(import_path);
-    }
-}
-
 fn does_source_exists_outside_sel_in_same_mod(
     def: Definition,
     ctx: &AssistContext,
@@ -1547,38 +1495,4 @@ mod modname {
         ",
         )
     }
-
-    #[test]
-    fn test_issue_11766() {
-        //https://github.com/rust-lang/rust-analyzer/issues/11766
-        check_assist(
-            extract_module,
-            r"
-            mod x {
-                pub struct Foo;
-                pub struct Bar;
-            }
-
-            use x::{Bar, Foo};
-
-            $0type A = (Foo, Bar);$0
-        ",
-            r"
-            mod x {
-                pub struct Foo;
-                pub struct Bar;
-            }
-
-            use x::{};
-
-            mod modname {
-                use super::x::Bar;
-
-                use super::x::Foo;
-
-                pub(crate) type A = (Foo, Bar);
-            }
-        ",
-        )
-    }
 }
diff --git a/crates/mbe/src/parser.rs b/crates/mbe/src/parser.rs
index 9be8d7085d..6c7be59841 100644
--- a/crates/mbe/src/parser.rs
+++ b/crates/mbe/src/parser.rs
@@ -135,6 +135,14 @@ fn next_op<'a>(first: &tt::TokenTree, src: &mut TtIter<'a>, mode: Mode) -> Resul
                         let id = lit.id;
                         Op::Var { name, kind, id }
                     }
+                    tt::Leaf::Punct(punct @ tt::Punct { char: '$', .. }) => match mode {
+                        Mode::Pattern => {
+                            return Err(ParseError::unexpected(
+                                "`$$` is not allowed on the pattern side",
+                            ))
+                        }
+                        Mode::Template => Op::Leaf(tt::Leaf::Punct(*punct)),
+                    },
                     tt::Leaf::Punct(_) | tt::Leaf::Literal(_) => {
                         return Err(ParseError::expected("expected ident"))
                     }
diff --git a/crates/parser/src/grammar/generic_params.rs b/crates/parser/src/grammar/generic_params.rs
index 78a75fad1c..fbd514078d 100644
--- a/crates/parser/src/grammar/generic_params.rs
+++ b/crates/parser/src/grammar/generic_params.rs
@@ -133,12 +133,6 @@ fn type_bound(p: &mut Parser) -> bool {
     match p.current() {
         LIFETIME_IDENT => lifetime(p),
         T![for] => types::for_type(p, false),
-        T![?] if p.nth_at(1, T![for]) => {
-            // test question_for_type_trait_bound
-            // fn f<T>() where T: ?for<> Sized {}
-            p.bump_any();
-            types::for_type(p, false)
-        }
         current => {
             match current {
                 T![?] => p.bump_any(),
diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs
index a9e79cc7c3..df61ba8eec 100644
--- a/crates/rust-analyzer/src/bin/main.rs
+++ b/crates/rust-analyzer/src/bin/main.rs
@@ -157,7 +157,7 @@ fn run_server() -> Result<()> {
     let (initialize_id, initialize_params) = connection.initialize_start()?;
     tracing::info!("InitializeParams: {}", initialize_params);
     let initialize_params =
-        from_json::<lsp_types::InitializeParams>("InitializeParams", &initialize_params)?;
+        from_json::<lsp_types::InitializeParams>("InitializeParams", initialize_params)?;
 
     let root_path = match initialize_params
         .root_uri
diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs
index 8f779df18d..aac8e9222d 100644
--- a/crates/rust-analyzer/src/diagnostics/to_proto.rs
+++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs
@@ -462,10 +462,9 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
                 message: "original diagnostic".to_string(),
             };
             for sub in &subdiagnostics {
-                let mut message = sub.related.message.clone();
-                // Change empty message to " ", as they greatly confuse VS Code.
-                if message.is_empty() {
-                    message = String::from(" ");
+                // Filter out empty/non-existent messages, as they greatly confuse VS Code.
+                if sub.related.message.is_empty() {
+                    continue;
                 }
                 diagnostics.push(MappedRustDiagnostic {
                     url: sub.related.location.uri.clone(),
@@ -476,7 +475,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
                         code: code.clone().map(lsp_types::NumberOrString::String),
                         code_description: code_description.clone(),
                         source: Some(source.clone()),
-                        message,
+                        message: sub.related.message.clone(),
                         related_information: Some(vec![back_ref.clone()]),
                         tags: None, // don't apply modifiers again
                         data: None,
diff --git a/crates/rust-analyzer/src/dispatch.rs b/crates/rust-analyzer/src/dispatch.rs
index 4d94630a56..d208ba16cb 100644
--- a/crates/rust-analyzer/src/dispatch.rs
+++ b/crates/rust-analyzer/src/dispatch.rs
@@ -1,12 +1,12 @@
 //! See [RequestDispatcher].
 use std::{fmt, panic, thread};
 
-use ide::Cancelled;
 use lsp_server::ExtractError;
 use serde::{de::DeserializeOwned, Serialize};
 
 use crate::{
     global_state::{GlobalState, GlobalStateSnapshot},
+    lsp_utils::is_cancelled,
     main_loop::Task,
     LspError, Result,
 };
@@ -37,40 +37,38 @@ impl<'a> RequestDispatcher<'a> {
     pub(crate) fn on_sync_mut<R>(
         &mut self,
         f: fn(&mut GlobalState, R::Params) -> Result<R::Result>,
-    ) -> &mut Self
+    ) -> Result<&mut Self>
     where
         R: lsp_types::request::Request,
         R::Params: DeserializeOwned + panic::UnwindSafe + fmt::Debug,
         R::Result: Serialize,
     {
-        let (req, params, panic_context) = match self.parse::<R>() {
+        let (id, params, panic_context) = match self.parse::<R>() {
             Some(it) => it,
-            None => return self,
-        };
-        let result = {
-            let _pctx = stdx::panic_context::enter(panic_context);
-            f(self.global_state, params)
+            None => return Ok(self),
         };
-        if let Ok(response) = result_to_response::<R>(req.id.clone(), result) {
-            self.global_state.respond(response);
-        }
+        let _pctx = stdx::panic_context::enter(panic_context);
 
-        self
+        let result = f(self.global_state, params);
+        let response = result_to_response::<R>(id, result);
+
+        self.global_state.respond(response);
+        Ok(self)
     }
 
     /// Dispatches the request onto the current thread.
     pub(crate) fn on_sync<R>(
         &mut self,
         f: fn(GlobalStateSnapshot, R::Params) -> Result<R::Result>,
-    ) -> &mut Self
+    ) -> Result<&mut Self>
     where
-        R: lsp_types::request::Request,
+        R: lsp_types::request::Request + 'static,
         R::Params: DeserializeOwned + panic::UnwindSafe + fmt::Debug,
         R::Result: Serialize,
     {
-        let (req, params, panic_context) = match self.parse::<R>() {
+        let (id, params, panic_context) = match self.parse::<R>() {
             Some(it) => it,
-            None => return self,
+            None => return Ok(self),
         };
         let global_state_snapshot = self.global_state.snapshot();
 
@@ -78,12 +76,10 @@ impl<'a> RequestDispatcher<'a> {
             let _pctx = stdx::panic_context::enter(panic_context);
             f(global_state_snapshot, params)
         });
+        let response = thread_result_to_response::<R>(id, result);
 
-        if let Ok(response) = thread_result_to_response::<R>(req.id.clone(), result) {
-            self.global_state.respond(response);
-        }
-
-        self
+        self.global_state.respond(response);
+        Ok(self)
     }
 
     /// Dispatches the request onto thread pool
@@ -96,7 +92,7 @@ impl<'a> RequestDispatcher<'a> {
         R::Params: DeserializeOwned + panic::UnwindSafe + Send + fmt::Debug,
         R::Result: Serialize,
     {
-        let (req, params, panic_context) = match self.parse::<R>() {
+        let (id, params, panic_context) = match self.parse::<R>() {
             Some(it) => it,
             None => return self,
         };
@@ -108,10 +104,8 @@ impl<'a> RequestDispatcher<'a> {
                     let _pctx = stdx::panic_context::enter(panic_context);
                     f(world, params)
                 });
-                match thread_result_to_response::<R>(req.id.clone(), result) {
-                    Ok(response) => Task::Response(response),
-                    Err(_) => Task::Retry(req),
-                }
+                let response = thread_result_to_response::<R>(id, result);
+                Task::Response(response)
             }
         });
 
@@ -130,7 +124,7 @@ impl<'a> RequestDispatcher<'a> {
         }
     }
 
-    fn parse<R>(&mut self) -> Option<(lsp_server::Request, R::Params, String)>
+    fn parse<R>(&mut self) -> Option<(lsp_server::RequestId, R::Params, String)>
     where
         R: lsp_types::request::Request,
         R::Params: DeserializeOwned + fmt::Debug,
@@ -140,12 +134,12 @@ impl<'a> RequestDispatcher<'a> {
             _ => return None,
         };
 
-        let res = crate::from_json(R::METHOD, &req.params);
+        let res = crate::from_json(R::METHOD, req.params);
         match res {
             Ok(params) => {
                 let panic_context =
                     format!("\nversion: {}\nrequest: {} {:#?}", env!("REV"), R::METHOD, params);
-                Some((req, params, panic_context))
+                Some((req.id, params, panic_context))
             }
             Err(err) => {
                 let response = lsp_server::Response::new_err(
@@ -163,7 +157,7 @@ impl<'a> RequestDispatcher<'a> {
 fn thread_result_to_response<R>(
     id: lsp_server::RequestId,
     result: thread::Result<Result<R::Result>>,
-) -> Result<lsp_server::Response, Cancelled>
+) -> lsp_server::Response
 where
     R: lsp_types::request::Request,
     R::Params: DeserializeOwned,
@@ -172,22 +166,19 @@ where
     match result {
         Ok(result) => result_to_response::<R>(id, result),
         Err(panic) => {
+            let mut message = "request handler panicked".to_string();
+
             let panic_message = panic
                 .downcast_ref::<String>()
                 .map(String::as_str)
                 .or_else(|| panic.downcast_ref::<&str>().copied());
 
-            let mut message = "request handler panicked".to_string();
             if let Some(panic_message) = panic_message {
                 message.push_str(": ");
                 message.push_str(panic_message)
             };
 
-            Ok(lsp_server::Response::new_err(
-                id,
-                lsp_server::ErrorCode::InternalError as i32,
-                message,
-            ))
+            lsp_server::Response::new_err(id, lsp_server::ErrorCode::InternalError as i32, message)
         }
     }
 }
@@ -195,27 +186,33 @@ where
 fn result_to_response<R>(
     id: lsp_server::RequestId,
     result: Result<R::Result>,
-) -> Result<lsp_server::Response, Cancelled>
+) -> lsp_server::Response
 where
     R: lsp_types::request::Request,
     R::Params: DeserializeOwned,
     R::Result: Serialize,
 {
-    let res = match result {
+    match result {
         Ok(resp) => lsp_server::Response::new_ok(id, &resp),
         Err(e) => match e.downcast::<LspError>() {
             Ok(lsp_error) => lsp_server::Response::new_err(id, lsp_error.code, lsp_error.message),
-            Err(e) => match e.downcast::<Cancelled>() {
-                Ok(cancelled) => return Err(*cancelled),
-                Err(e) => lsp_server::Response::new_err(
-                    id,
-                    lsp_server::ErrorCode::InternalError as i32,
-                    e.to_string(),
-                ),
-            },
+            Err(e) => {
+                if is_cancelled(&*e) {
+                    lsp_server::Response::new_err(
+                        id,
+                        lsp_server::ErrorCode::ContentModified as i32,
+                        "content modified".to_string(),
+                    )
+                } else {
+                    lsp_server::Response::new_err(
+                        id,
+                        lsp_server::ErrorCode::InternalError as i32,
+                        e.to_string(),
+                    )
+                }
+            }
         },
-    };
-    Ok(res)
+    }
 }
 
 pub(crate) struct NotificationDispatcher<'a> {
diff --git a/crates/rust-analyzer/src/from_proto.rs b/crates/rust-analyzer/src/from_proto.rs
index 7bdd34d1f0..ffc69a419a 100644
--- a/crates/rust-analyzer/src/from_proto.rs
+++ b/crates/rust-analyzer/src/from_proto.rs
@@ -91,7 +91,7 @@ pub(crate) fn annotation(
 ) -> Result<Annotation> {
     let data =
         code_lens.data.ok_or_else(|| invalid_params_error("code lens without data".to_string()))?;
-    let resolve = from_json::<lsp_ext::CodeLensResolveData>("CodeLensResolveData", &data)?;
+    let resolve = from_json::<lsp_ext::CodeLensResolveData>("CodeLensResolveData", data)?;
 
     match resolve {
         lsp_ext::CodeLensResolveData::Impls(params) => {
diff --git a/crates/rust-analyzer/src/lib.rs b/crates/rust-analyzer/src/lib.rs
index d5bc8f65f8..d6b37760a0 100644
--- a/crates/rust-analyzer/src/lib.rs
+++ b/crates/rust-analyzer/src/lib.rs
@@ -49,7 +49,7 @@ pub use crate::{caps::server_capabilities, main_loop::main_loop};
 pub type Error = Box<dyn std::error::Error + Send + Sync>;
 pub type Result<T, E = Error> = std::result::Result<T, E>;
 
-pub fn from_json<T: DeserializeOwned>(what: &'static str, json: &serde_json::Value) -> Result<T> {
+pub fn from_json<T: DeserializeOwned>(what: &'static str, json: serde_json::Value) -> Result<T> {
     let res = serde_json::from_value(json.clone())
         .map_err(|e| format!("Failed to deserialize {}: {}; {}", what, e, json))?;
     Ok(res)
diff --git a/crates/rust-analyzer/src/lsp_utils.rs b/crates/rust-analyzer/src/lsp_utils.rs
index 22bab8fa82..460ae4ef4d 100644
--- a/crates/rust-analyzer/src/lsp_utils.rs
+++ b/crates/rust-analyzer/src/lsp_utils.rs
@@ -1,6 +1,7 @@
 //! Utilities for LSP-related boilerplate code.
-use std::{ops::Range, sync::Arc};
+use std::{error::Error, ops::Range, sync::Arc};
 
+use ide_db::base_db::Cancelled;
 use lsp_server::Notification;
 
 use crate::{
@@ -14,6 +15,10 @@ pub(crate) fn invalid_params_error(message: String) -> LspError {
     LspError { code: lsp_server::ErrorCode::InvalidParams as i32, message }
 }
 
+pub(crate) fn is_cancelled(e: &(dyn Error + 'static)) -> bool {
+    e.downcast_ref::<Cancelled>().is_some()
+}
+
 pub(crate) fn notification_is<N: lsp_types::notification::Notification>(
     notification: &Notification,
 ) -> bool {
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index babcbb4782..3c87968743 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -8,7 +8,7 @@ use std::{
 
 use always_assert::always;
 use crossbeam_channel::{select, Receiver};
-use ide_db::base_db::{Cancelled, SourceDatabaseExt, VfsPath};
+use ide_db::base_db::{SourceDatabaseExt, VfsPath};
 use lsp_server::{Connection, Notification, Request};
 use lsp_types::notification::Notification as _;
 use vfs::{ChangeKind, FileId};
@@ -19,7 +19,7 @@ use crate::{
     from_proto,
     global_state::{file_id_to_url, url_to_file_id, GlobalState},
     handlers, lsp_ext,
-    lsp_utils::{apply_document_changes, notification_is, Progress},
+    lsp_utils::{apply_document_changes, is_cancelled, notification_is, Progress},
     mem_docs::DocumentData,
     reload::{self, BuildDataProgress, ProjectWorkspaceProgress},
     Result,
@@ -60,7 +60,6 @@ enum Event {
 #[derive(Debug)]
 pub(crate) enum Task {
     Response(lsp_server::Response),
-    Retry(lsp_server::Request),
     Diagnostics(Vec<(FileId, Vec<lsp_types::Diagnostic>)>),
     PrimeCaches(PrimeCachesProgress),
     FetchWorkspace(ProjectWorkspaceProgress),
@@ -197,7 +196,7 @@ impl GlobalState {
         let was_quiescent = self.is_quiescent();
         match event {
             Event::Lsp(msg) => match msg {
-                lsp_server::Message::Request(req) => self.on_request(loop_start, req),
+                lsp_server::Message::Request(req) => self.on_request(loop_start, req)?,
                 lsp_server::Message::Notification(not) => {
                     self.on_notification(not)?;
                 }
@@ -209,7 +208,6 @@ impl GlobalState {
                 loop {
                     match task {
                         Task::Response(response) => self.respond(response),
-                        Task::Retry(req) => self.on_request(loop_start, req),
                         Task::Diagnostics(diagnostics_per_file) => {
                             for (file_id, diagnostics) in diagnostics_per_file {
                                 self.diagnostics.set_native_diagnostics(file_id, diagnostics)
@@ -555,7 +553,7 @@ impl GlobalState {
         Ok(())
     }
 
-    fn on_request(&mut self, request_received: Instant, req: Request) {
+    fn on_request(&mut self, request_received: Instant, req: Request) -> Result<()> {
         self.register_request(&req, request_received);
 
         if self.shutdown_requested {
@@ -564,7 +562,8 @@ impl GlobalState {
                 lsp_server::ErrorCode::InvalidRequest as i32,
                 "Shutdown already requested.".to_owned(),
             ));
-            return;
+
+            return Ok(());
         }
 
         // Avoid flashing a bunch of unresolved references during initial load.
@@ -574,21 +573,21 @@ impl GlobalState {
                 lsp_server::ErrorCode::ContentModified as i32,
                 "waiting for cargo metadata or cargo check".to_owned(),
             ));
-            return;
+            return Ok(());
         }
 
         RequestDispatcher { req: Some(req), global_state: self }
             .on_sync_mut::<lsp_types::request::Shutdown>(|s, ()| {
                 s.shutdown_requested = true;
                 Ok(())
-            })
-            .on_sync_mut::<lsp_ext::ReloadWorkspace>(handlers::handle_workspace_reload)
-            .on_sync_mut::<lsp_ext::MemoryUsage>(handlers::handle_memory_usage)
-            .on_sync_mut::<lsp_ext::ShuffleCrateGraph>(handlers::handle_shuffle_crate_graph)
-            .on_sync::<lsp_ext::JoinLines>(handlers::handle_join_lines)
-            .on_sync::<lsp_ext::OnEnter>(handlers::handle_on_enter)
-            .on_sync::<lsp_types::request::SelectionRangeRequest>(handlers::handle_selection_range)
-            .on_sync::<lsp_ext::MatchingBrace>(handlers::handle_matching_brace)
+            })?
+            .on_sync_mut::<lsp_ext::ReloadWorkspace>(handlers::handle_workspace_reload)?
+            .on_sync_mut::<lsp_ext::MemoryUsage>(handlers::handle_memory_usage)?
+            .on_sync_mut::<lsp_ext::ShuffleCrateGraph>(handlers::handle_shuffle_crate_graph)?
+            .on_sync::<lsp_ext::JoinLines>(handlers::handle_join_lines)?
+            .on_sync::<lsp_ext::OnEnter>(handlers::handle_on_enter)?
+            .on_sync::<lsp_types::request::SelectionRangeRequest>(handlers::handle_selection_range)?
+            .on_sync::<lsp_ext::MatchingBrace>(handlers::handle_matching_brace)?
             .on::<lsp_ext::AnalyzerStatus>(handlers::handle_analyzer_status)
             .on::<lsp_ext::SyntaxTree>(handlers::handle_syntax_tree)
             .on::<lsp_ext::ViewHir>(handlers::handle_view_hir)
@@ -645,8 +644,8 @@ impl GlobalState {
             .on::<lsp_types::request::WillRenameFiles>(handlers::handle_will_rename_files)
             .on::<lsp_ext::Ssr>(handlers::handle_ssr)
             .finish();
+        Ok(())
     }
-
     fn on_notification(&mut self, not: Notification) -> Result<()> {
         NotificationDispatcher { not: Some(not), global_state: self }
             .on::<lsp_types::notification::Cancel>(|this, params| {
@@ -665,11 +664,11 @@ impl GlobalState {
             })?
             .on::<lsp_types::notification::DidOpenTextDocument>(|this, params| {
                 if let Ok(path) = from_proto::vfs_path(&params.text_document.uri) {
-                    let already_exists = this
+                    if this
                         .mem_docs
                         .insert(path.clone(), DocumentData::new(params.text_document.version))
-                        .is_err();
-                    if already_exists {
+                        .is_err()
+                    {
                         tracing::error!("duplicate DidOpenTextDocument: {}", path)
                     }
                     this.vfs
@@ -688,7 +687,7 @@ impl GlobalState {
                             doc.version = params.text_document.version;
                         }
                         None => {
-                            tracing::error!("unexpected DidChangeTextDocument: {}", path);
+                            tracing::error!("unexpected DidChangeTextDocument: {}; send DidOpenTextDocument first", path);
                             return Ok(());
                         }
                     };
@@ -722,8 +721,7 @@ impl GlobalState {
                 }
                 if let Ok(abs_path) = from_proto::abs_path(&params.text_document.uri) {
                     if reload::should_refresh_for_change(&abs_path, ChangeKind::Modify) {
-                        this.fetch_workspaces_queue
-                            .request_op(format!("DidSaveTextDocument {}", abs_path.display()));
+                        this.fetch_workspaces_queue.request_op(format!("DidSaveTextDocument {}", abs_path.display()));
                     }
                 }
                 Ok(())
@@ -752,10 +750,7 @@ impl GlobalState {
                                     // provide a configuration. This is handled in Config::update below.
                                     let mut config = Config::clone(&*this.config);
                                     if let Err(error) = config.update(json.take()) {
-                                        this.show_message(
-                                            lsp_types::MessageType::WARNING,
-                                            error.to_string(),
-                                        );
+                                        this.show_message(lsp_types::MessageType::WARNING, error.to_string());
                                     }
                                     this.update_configuration(config);
                                 }
@@ -797,7 +792,7 @@ impl GlobalState {
                 .filter_map(|file_id| {
                     handlers::publish_diagnostics(&snapshot, file_id)
                         .map_err(|err| {
-                            if err.is::<Cancelled>() {
+                            if !is_cancelled(&*err) {
                                 tracing::error!("failed to compute diagnostics: {:?}", err);
                             }
                         })
diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts
index 51781b4205..b03424dbb3 100644
--- a/editors/code/src/ctx.ts
+++ b/editors/code/src/ctx.ts
@@ -81,9 +81,7 @@ export class Ctx {
         let icon = "";
         switch (status.health) {
             case "ok":
-                this.statusBar.command = undefined;
                 this.statusBar.color = undefined;
-                this.statusBar.backgroundColor = undefined;
                 break;
             case "warning":
                 this.statusBar.tooltip += "\nClick to reload.";


Test patch:
diff --git a/crates/hir-def/src/macro_expansion_tests/mbe.rs b/crates/hir-def/src/macro_expansion_tests/mbe.rs
index befef6547c..93d2f648ae 100644
--- a/crates/hir-def/src/macro_expansion_tests/mbe.rs
+++ b/crates/hir-def/src/macro_expansion_tests/mbe.rs
@@ -1544,3 +1544,71 @@ struct Foo;
 "##]],
     )
 }
+
+#[test]
+fn test_dollar_dollar() {
+    check(
+        r#"
+macro_rules! register_struct { ($Struct:ident) => {
+    macro_rules! register_methods { ($$($method:ident),*) => {
+        macro_rules! implement_methods { ($$$$($$val:expr),*) => {
+            struct $Struct;
+            impl $Struct { $$(fn $method() -> &'static [u32] { &[$$$$($$$$val),*] })*}
+        }}
+    }}
+}}
+
+register_struct!(Foo);
+register_methods!(alpha, beta);
+implement_methods!(1, 2, 3);
+"#,
+        expect![[r#"
+macro_rules! register_struct { ($Struct:ident) => {
+    macro_rules! register_methods { ($$($method:ident),*) => {
+        macro_rules! implement_methods { ($$$$($$val:expr),*) => {
+            struct $Struct;
+            impl $Struct { $$(fn $method() -> &'static [u32] { &[$$$$($$$$val),*] })*}
+        }}
+    }}
+}}
+
+macro_rules !register_methods {
+    ($($method: ident), *) = > {
+        macro_rules!implement_methods {
+            ($$($val: expr), *) = > {
+                struct Foo;
+                impl Foo {
+                    $(fn $method()-> & 'static[u32] {
+                        &[$$($$val), *]
+                    }
+                    )*
+                }
+            }
+        }
+    }
+}
+macro_rules !implement_methods {
+    ($($val: expr), *) = > {
+        struct Foo;
+        impl Foo {
+            fn alpha()-> & 'static[u32] {
+                &[$($val), *]
+            }
+            fn beta()-> & 'static[u32] {
+                &[$($val), *]
+            }
+        }
+    }
+}
+struct Foo;
+impl Foo {
+    fn alpha() -> & 'static[u32] {
+        &[1, 2, 3]
+    }
+    fn beta() -> & 'static[u32] {
+        &[1, 2, 3]
+    }
+}
+"#]],
+    )
+}
diff --git a/crates/hir-def/src/macro_expansion_tests/mbe/meta_syntax.rs b/crates/hir-def/src/macro_expansion_tests/mbe/meta_syntax.rs
index 2de10ddbdf..636a66ad53 100644
--- a/crates/hir-def/src/macro_expansion_tests/mbe/meta_syntax.rs
+++ b/crates/hir-def/src/macro_expansion_tests/mbe/meta_syntax.rs
@@ -56,6 +56,9 @@ macro_rules! f2 { ($i:) => ($i) }
 f2!();
 macro_rules! f3 { ($i:_) => () }
 f3!();
+
+macro_rules! m1 { ($$i) => () }
+m1!();
 "#,
         expect![[r#"
 macro_rules! i1 { invalid }
@@ -74,6 +77,9 @@ macro_rules! f2 { ($i:) => ($i) }
 /* error: invalid macro definition: missing fragment specifier */
 macro_rules! f3 { ($i:_) => () }
 /* error: invalid macro definition: missing fragment specifier */
+
+macro_rules! m1 { ($$i) => () }
+/* error: invalid macro definition: `$$` is not allowed on the pattern side */
 "#]],
     )
 }
diff --git a/crates/parser/test_data/parser/inline/ok/0201_question_for_type_trait_bound.rast b/crates/parser/test_data/parser/inline/ok/0201_question_for_type_trait_bound.rast
deleted file mode 100644
index 56e2d1095d..0000000000
--- a/crates/parser/test_data/parser/inline/ok/0201_question_for_type_trait_bound.rast
+++ /dev/null
@@ -1,47 +0,0 @@
-SOURCE_FILE
-  FN
-    FN_KW "fn"
-    WHITESPACE " "
-    NAME
-      IDENT "f"
-    GENERIC_PARAM_LIST
-      L_ANGLE "<"
-      TYPE_PARAM
-        NAME
-          IDENT "T"
-      R_ANGLE ">"
-    PARAM_LIST
-      L_PAREN "("
-      R_PAREN ")"
-    WHITESPACE " "
-    WHERE_CLAUSE
-      WHERE_KW "where"
-      WHITESPACE " "
-      WHERE_PRED
-        PATH_TYPE
-          PATH
-            PATH_SEGMENT
-              NAME_REF
-                IDENT "T"
-        COLON ":"
-        WHITESPACE " "
-        TYPE_BOUND_LIST
-          TYPE_BOUND
-            QUESTION "?"
-            FOR_TYPE
-              FOR_KW "for"
-              GENERIC_PARAM_LIST
-                L_ANGLE "<"
-                R_ANGLE ">"
-              WHITESPACE " "
-              PATH_TYPE
-                PATH
-                  PATH_SEGMENT
-                    NAME_REF
-                      IDENT "Sized"
-    WHITESPACE " "
-    BLOCK_EXPR
-      STMT_LIST
-        L_CURLY "{"
-        R_CURLY "}"
-  WHITESPACE "\n"
diff --git a/crates/parser/test_data/parser/inline/ok/0201_question_for_type_trait_bound.rs b/crates/parser/test_data/parser/inline/ok/0201_question_for_type_trait_bound.rs
deleted file mode 100644
index f80dd90d44..0000000000
--- a/crates/parser/test_data/parser/inline/ok/0201_question_for_type_trait_bound.rs
+++ /dev/null
@@ -1,1 +0,0 @@
-fn f<T>() where T: ?for<> Sized {}