Repository: rust-lang/rust-analyzer
Pull request #12431 (2022-05-31 19:02:08)

fix: Fix completions disappearing when typing two keys in quick succession
With this PR we now retry requests if they get cancelled due to document changes.

This fixes the completions problem we have where completions seem to randomly disappear, see https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Frust-analyzer/topic/Completions.20not.20always.20appearing 

Fixes https://github.com/rust-lang/rust-analyzer/issues/10187
Fixes https://github.com/rust-lang/rust-analyzer/issues/7560
Fixes https://github.com/rust-lang/rust-analyzer/issues/12153


Patch:
diff --git a/crates/hir-def/src/expr.rs b/crates/hir-def/src/expr.rs
index bc06085894..97681abab1 100644
--- a/crates/hir-def/src/expr.rs
+++ b/crates/hir-def/src/expr.rs
@@ -52,7 +52,7 @@ impl FloatTypeWrapper {
 
 impl std::fmt::Display for FloatTypeWrapper {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        write!(f, "{:?}", f64::from_bits(self.0))
+        write!(f, "{}", f64::from_bits(self.0))
     }
 }
 
diff --git a/crates/hir-ty/src/chalk_db.rs b/crates/hir-ty/src/chalk_db.rs
index af2aa96574..6972db8495 100644
--- a/crates/hir-ty/src/chalk_db.rs
+++ b/crates/hir-ty/src/chalk_db.rs
@@ -410,10 +410,8 @@ pub(crate) fn associated_ty_data_query(
     let resolver = hir_def::resolver::HasResolver::resolver(type_alias, db.upcast());
     let ctx = crate::TyLoweringContext::new(db, &resolver)
         .with_type_param_mode(crate::lower::ParamLoweringMode::Variable);
-    let pro_ty = TyBuilder::assoc_type_projection(db, type_alias)
-        .fill_with_bound_vars(crate::DebruijnIndex::INNERMOST, 0)
-        .build();
-    let self_ty = TyKind::Alias(AliasTy::Projection(pro_ty)).intern(Interner);
+    let self_ty =
+        TyKind::BoundVar(BoundVar::new(crate::DebruijnIndex::INNERMOST, 0)).intern(Interner);
     let mut bounds: Vec<_> = type_alias_data
         .bounds
         .iter()
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs
index 116c1c480a..910aee7b05 100644
--- a/crates/ide/src/inlay_hints.rs
+++ b/crates/ide/src/inlay_hints.rs
@@ -117,32 +117,33 @@ pub(crate) fn inlay_hints(
 
     let mut acc = Vec::new();
 
-    if let Some(scope) = sema.scope(&file) {
-        let famous_defs = FamousDefs(&sema, scope.krate());
-
-        let hints = |node| hints(&mut acc, &famous_defs, config, file_id, node);
-        match range_limit {
-            Some(FileRange { range, .. }) => match file.covering_element(range) {
-                NodeOrToken::Token(_) => return acc,
-                NodeOrToken::Node(n) => n
-                    .descendants()
-                    .filter(|descendant| range.intersect(descendant.text_range()).is_some())
-                    .for_each(hints),
-            },
-            None => file.descendants().for_each(hints),
-        };
-    }
+    let hints = |node| hints(&mut acc, &sema, config, file_id, node);
+    match range_limit {
+        Some(FileRange { range, .. }) => match file.covering_element(range) {
+            NodeOrToken::Token(_) => return acc,
+            NodeOrToken::Node(n) => n
+                .descendants()
+                .filter(|descendant| range.intersect(descendant.text_range()).is_some())
+                .for_each(hints),
+        },
+        None => file.descendants().for_each(hints),
+    };
 
     acc
 }
 
 fn hints(
     hints: &mut Vec<InlayHint>,
-    famous_defs @ FamousDefs(sema, _): &FamousDefs,
+    sema: &Semantics<RootDatabase>,
     config: &InlayHintsConfig,
     file_id: FileId,
     node: SyntaxNode,
 ) {
+    let famous_defs = match sema.scope(&node) {
+        Some(it) => FamousDefs(sema, it.krate()),
+        None => return,
+    };
+
     closing_brace_hints(hints, sema, config, file_id, node.clone());
     match_ast! {
         match node {
@@ -167,18 +168,8 @@ fn hints(
                 }
                 Some(())
             },
-            ast::Item(it) => match it {
-                // FIXME: record impl lifetimes so they aren't being reused in assoc item lifetime inlay hints
-                ast::Item::Impl(_) => None,
-                ast::Item::Fn(it) => fn_lifetime_fn_hints(hints, config, it),
-                // static type elisions
-                ast::Item::Static(it) => implicit_static_hints(hints, config, Either::Left(it)),
-                ast::Item::Const(it) => implicit_static_hints(hints, config, Either::Right(it)),
-                _ => None,
-            },
-            // FIXME: fn-ptr type, dyn fn type, and trait object type elisions
-            ast::Type(_) => None,
-            _ => None,
+            ast::Fn(it) => lifetime_fn_hints(hints, config, it),
+            _ => Some(()),
         }
     };
 }
@@ -288,39 +279,7 @@ fn closing_brace_hints(
     None
 }
 
-fn implicit_static_hints(
-    acc: &mut Vec<InlayHint>,
-    config: &InlayHintsConfig,
-    statik_or_const: Either<ast::Static, ast::Const>,
-) -> Option<()> {
-    if config.lifetime_elision_hints != LifetimeElisionHints::Always {
-        return None;
-    }
-
-    if let Either::Right(it) = &statik_or_const {
-        if ast::AssocItemList::can_cast(
-            it.syntax().parent().map_or(SyntaxKind::EOF, |it| it.kind()),
-        ) {
-            return None;
-        }
-    }
-
-    if let Some(ast::Type::RefType(ty)) = statik_or_const.either(|it| it.ty(), |it| it.ty()) {
-        if ty.lifetime().is_none() {
-            let t = ty.amp_token()?;
-            acc.push(InlayHint {
-                range: t.text_range(),
-                kind: InlayKind::LifetimeHint,
-                label: "'static".to_owned(),
-                tooltip: Some(InlayTooltip::String("Elided static lifetime".into())),
-            });
-        }
-    }
-
-    Some(())
-}
-
-fn fn_lifetime_fn_hints(
+fn lifetime_fn_hints(
     acc: &mut Vec<InlayHint>,
     config: &InlayHintsConfig,
     func: ast::Fn,
@@ -2634,30 +2593,6 @@ impl () {
         );
     }
 
-    #[test]
-    fn hints_lifetimes_static() {
-        check_with_config(
-            InlayHintsConfig {
-                lifetime_elision_hints: LifetimeElisionHints::Always,
-                ..TEST_CONFIG
-            },
-            r#"
-trait Trait {}
-static S: &str = "";
-//        ^'static
-const C: &str = "";
-//       ^'static
-const C: &dyn Trait = panic!();
-//       ^'static
-
-impl () {
-    const C: &str = "";
-    const C: &dyn Trait = panic!();
-}
-"#,
-        );
-    }
-
     #[test]
     fn hints_implicit_reborrow() {
         check_with_config(
diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs
index df61ba8eec..a9e79cc7c3 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/dispatch.rs b/crates/rust-analyzer/src/dispatch.rs
index d208ba16cb..4d94630a56 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,38 +37,40 @@ impl<'a> RequestDispatcher<'a> {
     pub(crate) fn on_sync_mut<R>(
         &mut self,
         f: fn(&mut GlobalState, R::Params) -> Result<R::Result>,
-    ) -> Result<&mut Self>
+    ) -> &mut Self
     where
         R: lsp_types::request::Request,
         R::Params: DeserializeOwned + panic::UnwindSafe + fmt::Debug,
         R::Result: Serialize,
     {
-        let (id, params, panic_context) = match self.parse::<R>() {
+        let (req, params, panic_context) = match self.parse::<R>() {
             Some(it) => it,
-            None => return Ok(self),
+            None => return self,
         };
-        let _pctx = stdx::panic_context::enter(panic_context);
-
-        let result = f(self.global_state, params);
-        let response = result_to_response::<R>(id, result);
+        let result = {
+            let _pctx = stdx::panic_context::enter(panic_context);
+            f(self.global_state, params)
+        };
+        if let Ok(response) = result_to_response::<R>(req.id.clone(), result) {
+            self.global_state.respond(response);
+        }
 
-        self.global_state.respond(response);
-        Ok(self)
+        self
     }
 
     /// Dispatches the request onto the current thread.
     pub(crate) fn on_sync<R>(
         &mut self,
         f: fn(GlobalStateSnapshot, R::Params) -> Result<R::Result>,
-    ) -> Result<&mut Self>
+    ) -> &mut Self
     where
-        R: lsp_types::request::Request + 'static,
+        R: lsp_types::request::Request,
         R::Params: DeserializeOwned + panic::UnwindSafe + fmt::Debug,
         R::Result: Serialize,
     {
-        let (id, params, panic_context) = match self.parse::<R>() {
+        let (req, params, panic_context) = match self.parse::<R>() {
             Some(it) => it,
-            None => return Ok(self),
+            None => return self,
         };
         let global_state_snapshot = self.global_state.snapshot();
 
@@ -76,10 +78,12 @@ 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);
 
-        self.global_state.respond(response);
-        Ok(self)
+        if let Ok(response) = thread_result_to_response::<R>(req.id.clone(), result) {
+            self.global_state.respond(response);
+        }
+
+        self
     }
 
     /// Dispatches the request onto thread pool
@@ -92,7 +96,7 @@ impl<'a> RequestDispatcher<'a> {
         R::Params: DeserializeOwned + panic::UnwindSafe + Send + fmt::Debug,
         R::Result: Serialize,
     {
-        let (id, params, panic_context) = match self.parse::<R>() {
+        let (req, params, panic_context) = match self.parse::<R>() {
             Some(it) => it,
             None => return self,
         };
@@ -104,8 +108,10 @@ impl<'a> RequestDispatcher<'a> {
                     let _pctx = stdx::panic_context::enter(panic_context);
                     f(world, params)
                 });
-                let response = thread_result_to_response::<R>(id, result);
-                Task::Response(response)
+                match thread_result_to_response::<R>(req.id.clone(), result) {
+                    Ok(response) => Task::Response(response),
+                    Err(_) => Task::Retry(req),
+                }
             }
         });
 
@@ -124,7 +130,7 @@ impl<'a> RequestDispatcher<'a> {
         }
     }
 
-    fn parse<R>(&mut self) -> Option<(lsp_server::RequestId, R::Params, String)>
+    fn parse<R>(&mut self) -> Option<(lsp_server::Request, R::Params, String)>
     where
         R: lsp_types::request::Request,
         R::Params: DeserializeOwned + fmt::Debug,
@@ -134,12 +140,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.id, params, panic_context))
+                Some((req, params, panic_context))
             }
             Err(err) => {
                 let response = lsp_server::Response::new_err(
@@ -157,7 +163,7 @@ impl<'a> RequestDispatcher<'a> {
 fn thread_result_to_response<R>(
     id: lsp_server::RequestId,
     result: thread::Result<Result<R::Result>>,
-) -> lsp_server::Response
+) -> Result<lsp_server::Response, Cancelled>
 where
     R: lsp_types::request::Request,
     R::Params: DeserializeOwned,
@@ -166,19 +172,22 @@ 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)
             };
 
-            lsp_server::Response::new_err(id, lsp_server::ErrorCode::InternalError as i32, message)
+            Ok(lsp_server::Response::new_err(
+                id,
+                lsp_server::ErrorCode::InternalError as i32,
+                message,
+            ))
         }
     }
 }
@@ -186,33 +195,27 @@ where
 fn result_to_response<R>(
     id: lsp_server::RequestId,
     result: Result<R::Result>,
-) -> lsp_server::Response
+) -> Result<lsp_server::Response, Cancelled>
 where
     R: lsp_types::request::Request,
     R::Params: DeserializeOwned,
     R::Result: Serialize,
 {
-    match result {
+    let res = 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) => {
-                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(),
-                    )
-                }
-            }
+            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(),
+                ),
+            },
         },
-    }
+    };
+    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 ffc69a419a..7bdd34d1f0 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 d6b37760a0..d5bc8f65f8 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 460ae4ef4d..22bab8fa82 100644
--- a/crates/rust-analyzer/src/lsp_utils.rs
+++ b/crates/rust-analyzer/src/lsp_utils.rs
@@ -1,7 +1,6 @@
 //! Utilities for LSP-related boilerplate code.
-use std::{error::Error, ops::Range, sync::Arc};
+use std::{ops::Range, sync::Arc};
 
-use ide_db::base_db::Cancelled;
 use lsp_server::Notification;
 
 use crate::{
@@ -15,10 +14,6 @@ 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 3c87968743..0fc5fd9dfa 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::{SourceDatabaseExt, VfsPath};
+use ide_db::base_db::{Cancelled, 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, is_cancelled, notification_is, Progress},
+    lsp_utils::{apply_document_changes, notification_is, Progress},
     mem_docs::DocumentData,
     reload::{self, BuildDataProgress, ProjectWorkspaceProgress},
     Result,
@@ -60,6 +60,7 @@ 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),
@@ -196,7 +197,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)?;
                 }
@@ -208,6 +209,7 @@ 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)
@@ -553,7 +555,7 @@ impl GlobalState {
         Ok(())
     }
 
-    fn on_request(&mut self, request_received: Instant, req: Request) -> Result<()> {
+    fn on_request(&mut self, request_received: Instant, req: Request) {
         self.register_request(&req, request_received);
 
         if self.shutdown_requested {
@@ -562,8 +564,7 @@ impl GlobalState {
                 lsp_server::ErrorCode::InvalidRequest as i32,
                 "Shutdown already requested.".to_owned(),
             ));
-
-            return Ok(());
+            return;
         }
 
         // Avoid flashing a bunch of unresolved references during initial load.
@@ -573,21 +574,21 @@ impl GlobalState {
                 lsp_server::ErrorCode::ContentModified as i32,
                 "waiting for cargo metadata or cargo check".to_owned(),
             ));
-            return Ok(());
+            return;
         }
 
         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)
@@ -644,8 +645,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| {
@@ -792,7 +793,7 @@ impl GlobalState {
                 .filter_map(|file_id| {
                     handlers::publish_diagnostics(&snapshot, file_id)
                         .map_err(|err| {
-                            if !is_cancelled(&*err) {
+                            if err.is::<Cancelled>() {
                                 tracing::error!("failed to compute diagnostics: {:?}", err);
                             }
                         })
diff --git a/docs/dev/style.md b/docs/dev/style.md
index 82b6c749e4..13da88044c 100644
--- a/docs/dev/style.md
+++ b/docs/dev/style.md
@@ -69,11 +69,11 @@ After you are happy with the state of the code, please use [interactive rebase](
 Avoid @mentioning people in commit messages and pull request descriptions(they are added to commit message by bors).
 Such messages create a lot of duplicate notification traffic during rebases.
 
-If possible, write Pull Request titles and descriptions from the user's perspective:
+If possible, write commit messages from user's perspective:
 
 ```
 # GOOD
-Make goto definition work inside macros
+Goto definition works inside macros
 
 # BAD
 Use original span for FileId
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts
index fd9665b614..d07e47c8de 100644
--- a/editors/code/src/config.ts
+++ b/editors/code/src/config.ts
@@ -219,7 +219,7 @@ export async function updateConfig(config: vscode.WorkspaceConfiguration) {
                 },
             ];
             for (const { val, langVal, target } of valMatrix) {
-                const patch = (val: unknown) => {
+                const pred = (val: unknown) => {
                     // some of the updates we do only append "enable" or "custom"
                     // that means on the next run we would find these again, but as objects with
                     // these properties causing us to destroy the config
@@ -229,15 +229,15 @@ export async function updateConfig(config: vscode.WorkspaceConfiguration) {
                         !(
                             typeof val === "object" &&
                             val !== null &&
-                            (oldKey === "completion.snippets" || !val.hasOwnProperty("custom"))
+                            (val.hasOwnProperty("enable") || val.hasOwnProperty("custom"))
                         )
                     );
                 };
-                if (patch(val)) {
+                if (pred(val)) {
                     await config.update(newKey, val, target, false);
                     await config.update(oldKey, undefined, target, false);
                 }
-                if (patch(langVal)) {
+                if (pred(langVal)) {
                     await config.update(newKey, langVal, target, true);
                     await config.update(oldKey, undefined, target, true);
                 }
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-ty/src/tests/traits.rs b/crates/hir-ty/src/tests/traits.rs
index a11b026df5..b1d295ca34 100644
--- a/crates/hir-ty/src/tests/traits.rs
+++ b/crates/hir-ty/src/tests/traits.rs
@@ -1,7 +1,7 @@
 use cov_mark::check;
 use expect_test::expect;
 
-use super::{check, check_infer, check_infer_with_mismatches, check_no_mismatches, check_types};
+use super::{check, check_infer, check_infer_with_mismatches, check_types};
 
 #[test]
 fn infer_await() {
@@ -3316,42 +3316,6 @@ pub trait Deserialize {
     );
 }
 
-#[test]
-fn bin_op_with_rhs_is_self_for_assoc_bound() {
-    check_no_mismatches(
-        r#"//- minicore: eq
-        fn repro<T>(t: T) -> bool
-where
-    T: Request,
-    T::Output: Convertable,
-{
-    let a = execute(&t).convert();
-    let b = execute(&t).convert();
-    a.eq(&b);
-    let a = execute(&t).convert2();
-    let b = execute(&t).convert2();
-    a.eq(&b)
-}
-fn execute<T>(t: &T) -> T::Output
-where
-    T: Request,
-{
-    <T as Request>::output()
-}
-trait Convertable {
-    type TraitSelf: PartialEq<Self::TraitSelf>;
-    type AssocAsDefaultSelf: PartialEq;
-    fn convert(self) -> Self::AssocAsDefaultSelf;
-    fn convert2(self) -> Self::TraitSelf;
-}
-trait Request {
-    type Output;
-    fn output() -> Self::Output;
-}
-     "#,
-    );
-}
-
 #[test]
 fn bin_op_adt_with_rhs_primitive() {
     check_infer_with_mismatches(
diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs
index c95a0e4005..07f224da66 100644
--- a/crates/ide/src/hover/tests.rs
+++ b/crates/ide/src/hover/tests.rs
@@ -3634,7 +3634,7 @@ const FOO$0: f32 = 1f32;
             ```
 
             ```rust
-            const FOO: f32 = 1.0
+            const FOO: f32 = 1
             ```
 
             ---
@@ -3656,7 +3656,7 @@ const FOO$0: f64 = 1.0f64;
             ```
 
             ```rust
-            const FOO: f64 = 1.0
+            const FOO: f64 = 1
             ```
 
             ---