about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_ast/src/attr/mod.rs12
-rw-r--r--compiler/rustc_error_messages/locales/en-US/session.ftl10
-rw-r--r--compiler/rustc_errors/Cargo.toml2
-rw-r--r--compiler/rustc_middle/src/query/mod.rs10
-rw-r--r--compiler/rustc_session/src/errors.rs51
-rw-r--r--compiler/rustc_session/src/lib.rs2
-rw-r--r--compiler/rustc_session/src/output.rs35
-rw-r--r--compiler/rustc_session/src/session.rs44
-rw-r--r--library/core/src/num/uint_macros.rs2
-rw-r--r--src/etc/check_missing_items.py9
-rw-r--r--src/librustdoc/html/static/css/rustdoc.css6
-rw-r--r--src/librustdoc/json/mod.rs15
-rw-r--r--src/test/rustdoc-gui/search-result-display.goml3
-rw-r--r--src/test/rustdoc-json/traits/uses_extern_trait.rs7
-rw-r--r--src/tools/rust-analyzer/.github/workflows/release.yaml10
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/lower.rs75
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs28
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs1
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs37
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_type_alias.rs49
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_imports.rs8
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_bounds.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_use.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/utils.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/mod_.rs20
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs39
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/active_parameter.rs8
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs31
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/insert_whitespace_into_node.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/hover/render.rs21
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/hover/tests.rs58
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs182
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/lib.rs4
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/handlers.rs2
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs4
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/to_proto.rs57
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs22
39 files changed, 699 insertions, 181 deletions
diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs
index a40508494cd..28198e69bff 100644
--- a/compiler/rustc_ast/src/attr/mod.rs
+++ b/compiler/rustc_ast/src/attr/mod.rs
@@ -232,7 +232,8 @@ impl AttrItem {
 
 impl Attribute {
     /// Returns `true` if it is a sugared doc comment (`///` or `//!` for example).
-    /// So `#[doc = "doc"]` will return `false`.
+    /// So `#[doc = "doc"]` (which is a doc comment) and `#[doc(...)]` (which is not
+    /// a doc comment) will return `false`.
     pub fn is_doc_comment(&self) -> bool {
         match self.kind {
             AttrKind::Normal(..) => false,
@@ -240,6 +241,11 @@ impl Attribute {
         }
     }
 
+    /// Returns the documentation and its kind if this is a doc comment or a sugared doc comment.
+    /// * `///doc` returns `Some(("doc", CommentKind::Line))`.
+    /// * `/** doc */` returns `Some(("doc", CommentKind::Block))`.
+    /// * `#[doc = "doc"]` returns `Some(("doc", CommentKind::Line))`.
+    /// * `#[doc(...)]` returns `None`.
     pub fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)> {
         match self.kind {
             AttrKind::DocComment(kind, data) => Some((data, kind)),
@@ -252,6 +258,10 @@ impl Attribute {
         }
     }
 
+    /// Returns the documentation if this is a doc comment or a sugared doc comment.
+    /// * `///doc` returns `Some("doc")`.
+    /// * `#[doc = "doc"]` returns `Some("doc")`.
+    /// * `#[doc(...)]` returns `None`.
     pub fn doc_str(&self) -> Option<Symbol> {
         match self.kind {
             AttrKind::DocComment(.., data) => Some(data),
diff --git a/compiler/rustc_error_messages/locales/en-US/session.ftl b/compiler/rustc_error_messages/locales/en-US/session.ftl
index d2a2958f624..76cae3c81e4 100644
--- a/compiler/rustc_error_messages/locales/en-US/session.ftl
+++ b/compiler/rustc_error_messages/locales/en-US/session.ftl
@@ -56,3 +56,13 @@ session_target_invalid_bits_size = {$err}
 session_target_stack_protector_not_supported = `-Z stack-protector={$stack_protector}` is not supported for target {$target_triple} and will be ignored
 
 session_split_debuginfo_unstable_platform = `-Csplit-debuginfo={$debuginfo}` is unstable on this platform
+
+session_file_is_not_writeable = output file {$file} is not writeable -- check its permissions
+
+session_crate_name_does_not_match = `--crate-name` and `#[crate_name]` are required to match, but `{$s}` != `{$name}`
+
+session_crate_name_invalid = crate names cannot start with a `-`, but `{$s}` has a leading hyphen
+
+session_crate_name_empty = crate name must not be empty
+
+session_invalid_character_in_create_name = invalid character `{$character}` in crate name: `{$crate_name}`
diff --git a/compiler/rustc_errors/Cargo.toml b/compiler/rustc_errors/Cargo.toml
index 4d207fd17fb..c36ca11fad6 100644
--- a/compiler/rustc_errors/Cargo.toml
+++ b/compiler/rustc_errors/Cargo.toml
@@ -13,9 +13,9 @@ rustc_serialize = { path = "../rustc_serialize" }
 rustc_span = { path = "../rustc_span" }
 rustc_macros = { path = "../rustc_macros" }
 rustc_data_structures = { path = "../rustc_data_structures" }
+rustc_target = { path = "../rustc_target" }
 rustc_hir = { path = "../rustc_hir" }
 rustc_lint_defs = { path = "../rustc_lint_defs" }
-rustc_target = { path = "../rustc_target" }
 unicode-width = "0.1.4"
 atty = "0.2"
 termcolor = "1.0"
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index aaceec98b1a..0fe22a26cd5 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -1611,6 +1611,16 @@ rustc_queries! {
         desc { "looking up late bound vars" }
     }
 
+    /// Computes the visibility of the provided `def_id`.
+    ///
+    /// If the item from the `def_id` doesn't have a visibility, it will panic. For example
+    /// a generic type parameter will panic if you call this method on it:
+    ///
+    /// ```
+    /// pub trait Foo<T: Debug> {}
+    /// ```
+    ///
+    /// In here, if you call `visibility` on `T`, it'll panic.
     query visibility(def_id: DefId) -> ty::Visibility<DefId> {
         desc { |tcx| "computing visibility of `{}`", tcx.def_path_str(def_id) }
         separate_provide_extern
diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs
index 3c93cfab183..c6596ff2498 100644
--- a/compiler/rustc_session/src/errors.rs
+++ b/compiler/rustc_session/src/errors.rs
@@ -2,7 +2,7 @@ use std::num::NonZeroU32;
 
 use crate::cgu_reuse_tracker::CguReuse;
 use crate::{self as rustc_session, SessionDiagnostic};
-use rustc_errors::{fluent, DiagnosticBuilder, Handler, MultiSpan};
+use rustc_errors::{fluent, DiagnosticBuilder, ErrorGuaranteed, Handler, MultiSpan};
 use rustc_macros::SessionDiagnostic;
 use rustc_span::{Span, Symbol};
 use rustc_target::abi::TargetDataLayoutErrors;
@@ -170,3 +170,52 @@ pub struct StackProtectorNotSupportedForTarget<'a> {
 pub struct SplitDebugInfoUnstablePlatform {
     pub debuginfo: SplitDebuginfo,
 }
+
+#[derive(SessionDiagnostic)]
+#[diag(session::file_is_not_writeable)]
+pub struct FileIsNotWriteable<'a> {
+    pub file: &'a std::path::Path,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(session::crate_name_does_not_match)]
+pub struct CrateNameDoesNotMatch<'a> {
+    #[primary_span]
+    pub span: Span,
+    pub s: &'a str,
+    pub name: Symbol,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(session::crate_name_invalid)]
+pub struct CrateNameInvalid<'a> {
+    pub s: &'a str,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(session::crate_name_empty)]
+pub struct CrateNameEmpty {
+    #[primary_span]
+    pub span: Option<Span>,
+}
+
+pub struct InvalidCharacterInCrateName<'a> {
+    pub span: Option<Span>,
+    pub character: char,
+    pub crate_name: &'a str,
+}
+
+impl crate::SessionDiagnostic<'_> for InvalidCharacterInCrateName<'_> {
+    fn into_diagnostic(
+        self,
+        sess: &Handler,
+    ) -> rustc_errors::DiagnosticBuilder<'_, ErrorGuaranteed> {
+        let mut diag = sess.struct_err(fluent::session::invalid_character_in_create_name);
+        if let Some(sp) = self.span {
+            diag.set_span(sp);
+        }
+        diag.set_arg("character", self.character);
+        diag.set_arg("crate_name", self.crate_name);
+        diag
+    }
+}
diff --git a/compiler/rustc_session/src/lib.rs b/compiler/rustc_session/src/lib.rs
index 02d5d33c8d5..b9b243f6f08 100644
--- a/compiler/rustc_session/src/lib.rs
+++ b/compiler/rustc_session/src/lib.rs
@@ -9,6 +9,8 @@
 #![feature(map_many_mut)]
 #![recursion_limit = "256"]
 #![allow(rustc::potential_query_instability)]
+#![deny(rustc::untranslatable_diagnostic)]
+#![deny(rustc::diagnostic_outside_of_impl)]
 
 #[macro_use]
 extern crate rustc_macros;
diff --git a/compiler/rustc_session/src/output.rs b/compiler/rustc_session/src/output.rs
index e5e6579d75b..2511bee46af 100644
--- a/compiler/rustc_session/src/output.rs
+++ b/compiler/rustc_session/src/output.rs
@@ -1,5 +1,9 @@
 //! Related to out filenames of compilation (e.g. save analysis, binaries).
 use crate::config::{CrateType, Input, OutputFilenames, OutputType};
+use crate::errors::{
+    CrateNameDoesNotMatch, CrateNameEmpty, CrateNameInvalid, FileIsNotWriteable,
+    InvalidCharacterInCrateName,
+};
 use crate::Session;
 use rustc_ast as ast;
 use rustc_span::symbol::sym;
@@ -30,11 +34,7 @@ pub fn out_filename(
 /// read-only file.  We should be consistent.
 pub fn check_file_is_writeable(file: &Path, sess: &Session) {
     if !is_writeable(file) {
-        sess.fatal(&format!(
-            "output file {} is not writeable -- check its \
-                            permissions",
-            file.display()
-        ));
+        sess.emit_fatal(FileIsNotWriteable { file });
     }
 }
 
@@ -61,11 +61,7 @@ pub fn find_crate_name(sess: &Session, attrs: &[ast::Attribute], input: &Input)
     if let Some(ref s) = sess.opts.crate_name {
         if let Some((attr, name)) = attr_crate_name {
             if name.as_str() != s {
-                let msg = format!(
-                    "`--crate-name` and `#[crate_name]` are \
-                                   required to match, but `{s}` != `{name}`"
-                );
-                sess.span_err(attr.span, &msg);
+                sess.emit_err(CrateNameDoesNotMatch { span: attr.span, s, name });
             }
         }
         return validate(s.clone(), None);
@@ -77,11 +73,7 @@ pub fn find_crate_name(sess: &Session, attrs: &[ast::Attribute], input: &Input)
     if let Input::File(ref path) = *input {
         if let Some(s) = path.file_stem().and_then(|s| s.to_str()) {
             if s.starts_with('-') {
-                let msg = format!(
-                    "crate names cannot start with a `-`, but \
-                                   `{s}` has a leading hyphen"
-                );
-                sess.err(&msg);
+                sess.emit_err(CrateNameInvalid { s });
             } else {
                 return validate(s.replace('-', "_"), None);
             }
@@ -94,15 +86,9 @@ pub fn find_crate_name(sess: &Session, attrs: &[ast::Attribute], input: &Input)
 pub fn validate_crate_name(sess: &Session, s: &str, sp: Option<Span>) {
     let mut err_count = 0;
     {
-        let mut say = |s: &str| {
-            match sp {
-                Some(sp) => sess.span_err(sp, s),
-                None => sess.err(s),
-            };
-            err_count += 1;
-        };
         if s.is_empty() {
-            say("crate name must not be empty");
+            err_count += 1;
+            sess.emit_err(CrateNameEmpty { span: sp });
         }
         for c in s.chars() {
             if c.is_alphanumeric() {
@@ -111,7 +97,8 @@ pub fn validate_crate_name(sess: &Session, s: &str, sp: Option<Span>) {
             if c == '_' {
                 continue;
             }
-            say(&format!("invalid character `{c}` in crate name: `{s}`"));
+            err_count += 1;
+            sess.emit_err(InvalidCharacterInCrateName { span: sp, character: c, crate_name: s });
         }
     }
 
diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs
index 9ed4faccdb8..a001f87db00 100644
--- a/compiler/rustc_session/src/session.rs
+++ b/compiler/rustc_session/src/session.rs
@@ -297,6 +297,8 @@ impl Session {
     }
 
     #[rustc_lint_diagnostics]
+    #[allow(rustc::untranslatable_diagnostic)]
+    #[allow(rustc::diagnostic_outside_of_impl)]
     pub fn struct_span_warn<S: Into<MultiSpan>>(
         &self,
         sp: S,
@@ -305,6 +307,8 @@ impl Session {
         self.diagnostic().struct_span_warn(sp, msg)
     }
     #[rustc_lint_diagnostics]
+    #[allow(rustc::untranslatable_diagnostic)]
+    #[allow(rustc::diagnostic_outside_of_impl)]
     pub fn struct_span_warn_with_expectation<S: Into<MultiSpan>>(
         &self,
         sp: S,
@@ -314,6 +318,8 @@ impl Session {
         self.diagnostic().struct_span_warn_with_expectation(sp, msg, id)
     }
     #[rustc_lint_diagnostics]
+    #[allow(rustc::untranslatable_diagnostic)]
+    #[allow(rustc::diagnostic_outside_of_impl)]
     pub fn struct_span_warn_with_code<S: Into<MultiSpan>>(
         &self,
         sp: S,
@@ -323,10 +329,14 @@ impl Session {
         self.diagnostic().struct_span_warn_with_code(sp, msg, code)
     }
     #[rustc_lint_diagnostics]
+    #[allow(rustc::untranslatable_diagnostic)]
+    #[allow(rustc::diagnostic_outside_of_impl)]
     pub fn struct_warn(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> {
         self.diagnostic().struct_warn(msg)
     }
     #[rustc_lint_diagnostics]
+    #[allow(rustc::untranslatable_diagnostic)]
+    #[allow(rustc::diagnostic_outside_of_impl)]
     pub fn struct_warn_with_expectation(
         &self,
         msg: impl Into<DiagnosticMessage>,
@@ -335,6 +345,8 @@ impl Session {
         self.diagnostic().struct_warn_with_expectation(msg, id)
     }
     #[rustc_lint_diagnostics]
+    #[allow(rustc::untranslatable_diagnostic)]
+    #[allow(rustc::diagnostic_outside_of_impl)]
     pub fn struct_span_allow<S: Into<MultiSpan>>(
         &self,
         sp: S,
@@ -343,10 +355,14 @@ impl Session {
         self.diagnostic().struct_span_allow(sp, msg)
     }
     #[rustc_lint_diagnostics]
+    #[allow(rustc::untranslatable_diagnostic)]
+    #[allow(rustc::diagnostic_outside_of_impl)]
     pub fn struct_allow(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> {
         self.diagnostic().struct_allow(msg)
     }
     #[rustc_lint_diagnostics]
+    #[allow(rustc::untranslatable_diagnostic)]
+    #[allow(rustc::diagnostic_outside_of_impl)]
     pub fn struct_expect(
         &self,
         msg: impl Into<DiagnosticMessage>,
@@ -355,6 +371,8 @@ impl Session {
         self.diagnostic().struct_expect(msg, id)
     }
     #[rustc_lint_diagnostics]
+    #[allow(rustc::untranslatable_diagnostic)]
+    #[allow(rustc::diagnostic_outside_of_impl)]
     pub fn struct_span_err<S: Into<MultiSpan>>(
         &self,
         sp: S,
@@ -363,6 +381,8 @@ impl Session {
         self.diagnostic().struct_span_err(sp, msg)
     }
     #[rustc_lint_diagnostics]
+    #[allow(rustc::untranslatable_diagnostic)]
+    #[allow(rustc::diagnostic_outside_of_impl)]
     pub fn struct_span_err_with_code<S: Into<MultiSpan>>(
         &self,
         sp: S,
@@ -373,6 +393,8 @@ impl Session {
     }
     // FIXME: This method should be removed (every error should have an associated error code).
     #[rustc_lint_diagnostics]
+    #[allow(rustc::untranslatable_diagnostic)]
+    #[allow(rustc::diagnostic_outside_of_impl)]
     pub fn struct_err(
         &self,
         msg: impl Into<DiagnosticMessage>,
@@ -380,6 +402,8 @@ impl Session {
         self.parse_sess.struct_err(msg)
     }
     #[rustc_lint_diagnostics]
+    #[allow(rustc::untranslatable_diagnostic)]
+    #[allow(rustc::diagnostic_outside_of_impl)]
     pub fn struct_err_with_code(
         &self,
         msg: impl Into<DiagnosticMessage>,
@@ -388,6 +412,8 @@ impl Session {
         self.diagnostic().struct_err_with_code(msg, code)
     }
     #[rustc_lint_diagnostics]
+    #[allow(rustc::untranslatable_diagnostic)]
+    #[allow(rustc::diagnostic_outside_of_impl)]
     pub fn struct_warn_with_code(
         &self,
         msg: impl Into<DiagnosticMessage>,
@@ -396,6 +422,8 @@ impl Session {
         self.diagnostic().struct_warn_with_code(msg, code)
     }
     #[rustc_lint_diagnostics]
+    #[allow(rustc::untranslatable_diagnostic)]
+    #[allow(rustc::diagnostic_outside_of_impl)]
     pub fn struct_span_fatal<S: Into<MultiSpan>>(
         &self,
         sp: S,
@@ -404,6 +432,8 @@ impl Session {
         self.diagnostic().struct_span_fatal(sp, msg)
     }
     #[rustc_lint_diagnostics]
+    #[allow(rustc::untranslatable_diagnostic)]
+    #[allow(rustc::diagnostic_outside_of_impl)]
     pub fn struct_span_fatal_with_code<S: Into<MultiSpan>>(
         &self,
         sp: S,
@@ -413,15 +443,21 @@ impl Session {
         self.diagnostic().struct_span_fatal_with_code(sp, msg, code)
     }
     #[rustc_lint_diagnostics]
+    #[allow(rustc::untranslatable_diagnostic)]
+    #[allow(rustc::diagnostic_outside_of_impl)]
     pub fn struct_fatal(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, !> {
         self.diagnostic().struct_fatal(msg)
     }
 
     #[rustc_lint_diagnostics]
+    #[allow(rustc::untranslatable_diagnostic)]
+    #[allow(rustc::diagnostic_outside_of_impl)]
     pub fn span_fatal<S: Into<MultiSpan>>(&self, sp: S, msg: impl Into<DiagnosticMessage>) -> ! {
         self.diagnostic().span_fatal(sp, msg)
     }
     #[rustc_lint_diagnostics]
+    #[allow(rustc::untranslatable_diagnostic)]
+    #[allow(rustc::diagnostic_outside_of_impl)]
     pub fn span_fatal_with_code<S: Into<MultiSpan>>(
         &self,
         sp: S,
@@ -431,10 +467,14 @@ impl Session {
         self.diagnostic().span_fatal_with_code(sp, msg, code)
     }
     #[rustc_lint_diagnostics]
+    #[allow(rustc::untranslatable_diagnostic)]
+    #[allow(rustc::diagnostic_outside_of_impl)]
     pub fn fatal(&self, msg: impl Into<DiagnosticMessage>) -> ! {
         self.diagnostic().fatal(msg).raise()
     }
     #[rustc_lint_diagnostics]
+    #[allow(rustc::untranslatable_diagnostic)]
+    #[allow(rustc::diagnostic_outside_of_impl)]
     pub fn span_err_or_warn<S: Into<MultiSpan>>(
         &self,
         is_warning: bool,
@@ -448,6 +488,8 @@ impl Session {
         }
     }
     #[rustc_lint_diagnostics]
+    #[allow(rustc::untranslatable_diagnostic)]
+    #[allow(rustc::diagnostic_outside_of_impl)]
     pub fn span_err<S: Into<MultiSpan>>(
         &self,
         sp: S,
@@ -456,6 +498,8 @@ impl Session {
         self.diagnostic().span_err(sp, msg)
     }
     #[rustc_lint_diagnostics]
+    #[allow(rustc::untranslatable_diagnostic)]
+    #[allow(rustc::diagnostic_outside_of_impl)]
     pub fn span_err_with_code<S: Into<MultiSpan>>(
         &self,
         sp: S,
diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs
index aa3e8b9974e..46fd7f2d0e4 100644
--- a/library/core/src/num/uint_macros.rs
+++ b/library/core/src/num/uint_macros.rs
@@ -688,7 +688,7 @@ macro_rules! uint_impl {
         /// rounded down.
         ///
         /// This method might not be optimized owing to implementation details;
-        /// `log2` can produce results more efficiently for base 2, and `log10`
+        /// `ilog2` can produce results more efficiently for base 2, and `ilog10`
         /// can produce results more efficiently for base 10.
         ///
         /// # Panics
diff --git a/src/etc/check_missing_items.py b/src/etc/check_missing_items.py
index 991c881bae1..0026c4cbdca 100644
--- a/src/etc/check_missing_items.py
+++ b/src/etc/check_missing_items.py
@@ -49,6 +49,8 @@ def check_generic_param(param):
         ty = param["kind"]["type"]
         if ty["default"]:
             check_type(ty["default"])
+        for bound in ty["bounds"]:
+            check_generic_bound(bound)
     elif "const" in param["kind"]:
         check_type(param["kind"]["const"])
 
@@ -88,8 +90,11 @@ def check_path(path):
                 check_type(input_ty)
             if args["parenthesized"]["output"]:
                 check_type(args["parenthesized"]["output"])
-    if not valid_id(path["id"]):
-        print("Type contained an invalid ID:", path["id"])
+
+    if path["id"] in crate["index"]:
+        work_list.add(path["id"])
+    elif path["id"] not in crate["paths"]:
+        print("Id not in index or paths:", path["id"])
         sys.exit(1)
 
 def check_type(ty):
diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css
index 011c559b34b..600e7cf3a0b 100644
--- a/src/librustdoc/html/static/css/rustdoc.css
+++ b/src/librustdoc/html/static/css/rustdoc.css
@@ -986,12 +986,6 @@ so that we can apply CSS-filters to change the arrow color in themes */
 	padding-right: 1em;
 }
 
-.search-results .result-name > span {
-	display: inline-block;
-	margin: 0;
-	font-weight: normal;
-}
-
 .popover {
 	font-size: 1rem;
 	position: absolute;
diff --git a/src/librustdoc/json/mod.rs b/src/librustdoc/json/mod.rs
index 6a1409c7394..5e8f5f6fe3e 100644
--- a/src/librustdoc/json/mod.rs
+++ b/src/librustdoc/json/mod.rs
@@ -101,6 +101,7 @@ impl<'tcx> JsonRenderer<'tcx> {
     }
 
     fn get_trait_items(&mut self) -> Vec<(types::Id, types::Item)> {
+        debug!("Adding foreign trait items");
         Rc::clone(&self.cache)
             .traits
             .iter()
@@ -109,6 +110,7 @@ impl<'tcx> JsonRenderer<'tcx> {
                 if !id.is_local() {
                     let trait_item = &trait_item.trait_;
                     for item in &trait_item.items {
+                        trace!("Adding subitem to {id:?}: {:?}", item.item_id);
                         self.item(item.clone()).unwrap();
                     }
                     let item_id = from_item_id(id.into(), self.tcx);
@@ -184,7 +186,9 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> {
     /// the hashmap because certain items (traits and types) need to have their mappings for trait
     /// implementations filled out before they're inserted.
     fn item(&mut self, item: clean::Item) -> Result<(), Error> {
-        trace!("rendering {} {:?}", item.type_(), item.name);
+        let item_type = item.type_();
+        let item_name = item.name;
+        trace!("rendering {} {:?}", item_type, item_name);
 
         // Flatten items that recursively store other items. We include orphaned items from
         // stripped modules and etc that are otherwise reachable.
@@ -253,6 +257,7 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> {
             }
         }
 
+        trace!("done rendering {} {:?}", item_type, item_name);
         Ok(())
     }
 
@@ -263,14 +268,20 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> {
     fn after_krate(&mut self) -> Result<(), Error> {
         debug!("Done with crate");
 
+        debug!("Adding Primitve impls");
         for primitive in Rc::clone(&self.cache).primitive_locations.values() {
             self.get_impls(*primitive);
         }
 
         let e = ExternalCrate { crate_num: LOCAL_CRATE };
 
+        // FIXME(adotinthevoid): Remove this, as it's not consistant with not
+        // inlining foreign items.
+        let foreign_trait_items = self.get_trait_items();
         let mut index = (*self.index).clone().into_inner();
-        index.extend(self.get_trait_items());
+        index.extend(foreign_trait_items);
+
+        debug!("Constructing Output");
         // This needs to be the default HashMap for compatibility with the public interface for
         // rustdoc-json-types
         #[allow(rustc::default_hash_types)]
diff --git a/src/test/rustdoc-gui/search-result-display.goml b/src/test/rustdoc-gui/search-result-display.goml
index 54482005fa6..efbbfb925bd 100644
--- a/src/test/rustdoc-gui/search-result-display.goml
+++ b/src/test/rustdoc-gui/search-result-display.goml
@@ -13,6 +13,9 @@ size: (600, 100)
 // when computed it's larger.
 assert-css: (".search-results div.desc", {"width": "566px"})
 
+// The result set is all on one line.
+assert-css: (".search-results .result-name > span", {"display": "inline"})
+
 // Check that the crate filter `<select>` is correctly handled when it goes to next line.
 // To do so we need to update the length of one of its `<option>`.
 size: (900, 900)
diff --git a/src/test/rustdoc-json/traits/uses_extern_trait.rs b/src/test/rustdoc-json/traits/uses_extern_trait.rs
new file mode 100644
index 00000000000..430dd1543f5
--- /dev/null
+++ b/src/test/rustdoc-json/traits/uses_extern_trait.rs
@@ -0,0 +1,7 @@
+#![no_std]
+pub fn drop_default<T: core::default::Default>(_x: T) {}
+
+// FIXME(adotinthevoid): Theses shouldn't be here
+// @has "$.index[*][?(@.name=='Debug')]"
+// @set Debug_fmt = "$.index[*][?(@.name=='Debug')].inner.items[*]"
+// @has "$.index[*][?(@.name=='fmt')].id" $Debug_fmt
diff --git a/src/tools/rust-analyzer/.github/workflows/release.yaml b/src/tools/rust-analyzer/.github/workflows/release.yaml
index 303a10615bb..f4d472e3d5c 100644
--- a/src/tools/rust-analyzer/.github/workflows/release.yaml
+++ b/src/tools/rust-analyzer/.github/workflows/release.yaml
@@ -34,6 +34,7 @@ jobs:
           - os: ubuntu-20.04
             target: x86_64-unknown-linux-gnu
             code-target: linux-x64
+            container: ubuntu:18.04
           - os: ubuntu-20.04
             target: aarch64-unknown-linux-gnu
             code-target: linux-arm64
@@ -49,6 +50,7 @@ jobs:
 
     name: dist (${{ matrix.target }})
     runs-on: ${{ matrix.os }}
+    container: ${{ matrix.container }}
 
     env:
       RA_TARGET: ${{ matrix.target }}
@@ -59,6 +61,14 @@ jobs:
         with:
           fetch-depth: ${{ env.FETCH_DEPTH }}
 
+      - name: Install toolchain dependencies
+        if: matrix.container == 'ubuntu:18.04'
+        shell: bash
+        run: |
+          apt-get update && apt-get install -y build-essential curl
+          curl --proto '=https' --tlsv1.2 --retry 10 --retry-connrefused -fsSL "https://sh.rustup.rs" | sh -s -- --profile minimal --default-toolchain none -y
+          echo "${CARGO_HOME:-$HOME/.cargo}/bin" >> $GITHUB_PATH
+
       - name: Install Rust toolchain
         run: |
           rustup update --no-self-update stable
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs
index a9c124b42dc..4a5533c6487 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs
@@ -164,6 +164,8 @@ impl TyExt for Ty {
 
     fn dyn_trait(&self) -> Option<TraitId> {
         let trait_ref = match self.kind(Interner) {
+            // The principal trait bound should be the first element of the bounds. This is an
+            // invariant ensured by `TyLoweringContext::lower_dyn_trait()`.
             TyKind::Dyn(dyn_ty) => dyn_ty.bounds.skip_binders().interned().get(0).and_then(|b| {
                 match b.skip_binders() {
                     WhereClause::Implemented(trait_ref) => Some(trait_ref),
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs
index 4a37a794533..532544fee59 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs
@@ -981,43 +981,72 @@ impl<'a> TyLoweringContext<'a> {
 
     fn lower_dyn_trait(&self, bounds: &[Interned<TypeBound>]) -> Ty {
         let self_ty = TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)).intern(Interner);
+        // INVARIANT: The principal trait bound must come first. Others may be in any order but
+        // should be in the same order for the same set but possibly different order of bounds in
+        // the input.
+        // This invariant is used by `TyExt::dyn_trait()` and chalk.
         let bounds = self.with_shifted_in(DebruijnIndex::ONE, |ctx| {
-            let bounds =
-                bounds.iter().flat_map(|b| ctx.lower_type_bound(b, self_ty.clone(), false));
-
-            let mut auto_traits = SmallVec::<[_; 8]>::new();
-            let mut regular_traits = SmallVec::<[_; 2]>::new();
-            let mut other_bounds = SmallVec::<[_; 8]>::new();
-            for bound in bounds {
-                if let Some(id) = bound.trait_id() {
-                    if ctx.db.trait_data(from_chalk_trait_id(id)).is_auto {
-                        auto_traits.push(bound);
-                    } else {
-                        regular_traits.push(bound);
+            let mut bounds: Vec<_> = bounds
+                .iter()
+                .flat_map(|b| ctx.lower_type_bound(b, self_ty.clone(), false))
+                .collect();
+
+            let mut multiple_regular_traits = false;
+            let mut multiple_same_projection = false;
+            bounds.sort_unstable_by(|lhs, rhs| {
+                use std::cmp::Ordering;
+                match (lhs.skip_binders(), rhs.skip_binders()) {
+                    (WhereClause::Implemented(lhs), WhereClause::Implemented(rhs)) => {
+                        let lhs_id = lhs.trait_id;
+                        let lhs_is_auto = ctx.db.trait_data(from_chalk_trait_id(lhs_id)).is_auto;
+                        let rhs_id = rhs.trait_id;
+                        let rhs_is_auto = ctx.db.trait_data(from_chalk_trait_id(rhs_id)).is_auto;
+
+                        if !lhs_is_auto && !rhs_is_auto {
+                            multiple_regular_traits = true;
+                        }
+                        // Note that the ordering here is important; this ensures the invariant
+                        // mentioned above.
+                        (lhs_is_auto, lhs_id).cmp(&(rhs_is_auto, rhs_id))
                     }
-                } else {
-                    other_bounds.push(bound);
+                    (WhereClause::Implemented(_), _) => Ordering::Less,
+                    (_, WhereClause::Implemented(_)) => Ordering::Greater,
+                    (WhereClause::AliasEq(lhs), WhereClause::AliasEq(rhs)) => {
+                        match (&lhs.alias, &rhs.alias) {
+                            (AliasTy::Projection(lhs_proj), AliasTy::Projection(rhs_proj)) => {
+                                // We only compare the `associated_ty_id`s. We shouldn't have
+                                // multiple bounds for an associated type in the correct Rust code,
+                                // and if we do, we error out.
+                                if lhs_proj.associated_ty_id == rhs_proj.associated_ty_id {
+                                    multiple_same_projection = true;
+                                }
+                                lhs_proj.associated_ty_id.cmp(&rhs_proj.associated_ty_id)
+                            }
+                            // We don't produce `AliasTy::Opaque`s yet.
+                            _ => unreachable!(),
+                        }
+                    }
+                    // We don't produce `WhereClause::{TypeOutlives, LifetimeOutlives}` yet.
+                    _ => unreachable!(),
                 }
-            }
+            });
 
-            if regular_traits.len() > 1 {
+            if multiple_regular_traits || multiple_same_projection {
                 return None;
             }
 
-            auto_traits.sort_unstable_by_key(|b| b.trait_id().unwrap());
-            auto_traits.dedup();
+            // As multiple occurrences of the same auto traits *are* permitted, we dedulicate the
+            // bounds. We shouldn't have repeated elements besides auto traits at this point.
+            bounds.dedup();
 
-            Some(QuantifiedWhereClauses::from_iter(
-                Interner,
-                regular_traits.into_iter().chain(other_bounds).chain(auto_traits),
-            ))
+            Some(QuantifiedWhereClauses::from_iter(Interner, bounds))
         });
 
         if let Some(bounds) = bounds {
             let bounds = crate::make_single_type_binders(bounds);
             TyKind::Dyn(DynTy { bounds, lifetime: static_lifetime() }).intern(Interner)
         } else {
-            // FIXME: report error (additional non-auto traits)
+            // FIXME: report error (additional non-auto traits or associated type rebound)
             TyKind::Error.intern(Interner)
         }
     }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs
index e67c27aa2db..21a86319763 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs
@@ -3901,6 +3901,34 @@ fn g(t: &(dyn Sync + T<Proj = ()> + Send)) {
 }
 
 #[test]
+fn dyn_multiple_projection_bounds() {
+    check_no_mismatches(
+        r#"
+trait Trait {
+    type T;
+    type U;
+}
+
+fn f(t: &dyn Trait<T = (), U = ()>) {}
+fn g(t: &dyn Trait<U = (), T = ()>) {
+    f(t);
+}
+        "#,
+    );
+
+    check_types(
+        r#"
+trait Trait {
+    type T;
+}
+
+fn f(t: &dyn Trait<T = (), T = ()>) {}
+   //^&{unknown}
+        "#,
+    );
+}
+
+#[test]
 fn dyn_duplicate_auto_trait() {
     check_no_mismatches(
         r#"
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs
index b16f6fe03ae..1a7919a5a10 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs
@@ -5,6 +5,7 @@ use hir::{Adt, Crate, HasAttrs, HasSource, ModuleDef, Semantics};
 use ide_db::RootDatabase;
 use ide_db::{famous_defs::FamousDefs, helpers::mod_path_to_ast};
 use itertools::Itertools;
+use syntax::ast::edit_in_place::Removable;
 use syntax::ast::{self, make, AstNode, HasName, MatchArmList, MatchExpr, Pat};
 
 use crate::{
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs
index 96890ad51a6..9f51cdaf8b1 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs
@@ -7,6 +7,7 @@ use ide_db::{
     imports::insert_use::remove_path_if_in_use_stmt,
     path_transform::PathTransform,
     search::{FileReference, SearchScope},
+    source_change::SourceChangeBuilder,
     syntax_helpers::{insert_whitespace_into_node::insert_ws_into, node_ext::expr_as_name_ref},
     RootDatabase,
 };
@@ -100,18 +101,7 @@ pub(crate) fn inline_into_callers(acc: &mut Assists, ctx: &AssistContext<'_>) ->
                 builder.edit_file(file_id);
                 let count = refs.len();
                 // The collects are required as we are otherwise iterating while mutating 🙅‍♀️🙅‍♂️
-                let (name_refs, name_refs_use): (Vec<_>, Vec<_>) = refs
-                    .into_iter()
-                    .filter_map(|file_ref| match file_ref.name {
-                        ast::NameLike::NameRef(name_ref) => Some(name_ref),
-                        _ => None,
-                    })
-                    .partition_map(|name_ref| {
-                        match name_ref.syntax().ancestors().find_map(ast::UseTree::cast) {
-                            Some(use_tree) => Either::Right(builder.make_mut(use_tree)),
-                            None => Either::Left(name_ref),
-                        }
-                    });
+                let (name_refs, name_refs_use) = split_refs_and_uses(builder, refs, Some);
                 let call_infos: Vec<_> = name_refs
                     .into_iter()
                     .filter_map(CallInfo::from_name_ref)
@@ -130,11 +120,7 @@ pub(crate) fn inline_into_callers(acc: &mut Assists, ctx: &AssistContext<'_>) ->
                     .count();
                 if replaced + name_refs_use.len() == count {
                     // we replaced all usages in this file, so we can remove the imports
-                    name_refs_use.into_iter().for_each(|use_tree| {
-                        if let Some(path) = use_tree.path() {
-                            remove_path_if_in_use_stmt(&path);
-                        }
-                    })
+                    name_refs_use.iter().for_each(remove_path_if_in_use_stmt);
                 } else {
                     remove_def = false;
                 }
@@ -153,6 +139,23 @@ pub(crate) fn inline_into_callers(acc: &mut Assists, ctx: &AssistContext<'_>) ->
     )
 }
 
+pub(super) fn split_refs_and_uses<T: ast::AstNode>(
+    builder: &mut SourceChangeBuilder,
+    iter: impl IntoIterator<Item = FileReference>,
+    mut map_ref: impl FnMut(ast::NameRef) -> Option<T>,
+) -> (Vec<T>, Vec<ast::Path>) {
+    iter.into_iter()
+        .filter_map(|file_ref| match file_ref.name {
+            ast::NameLike::NameRef(name_ref) => Some(name_ref),
+            _ => None,
+        })
+        .filter_map(|name_ref| match name_ref.syntax().ancestors().find_map(ast::UseTree::cast) {
+            Some(use_tree) => builder.make_mut(use_tree).path().map(Either::Right),
+            None => map_ref(name_ref).map(Either::Left),
+        })
+        .partition_map(|either| either)
+}
+
 // Assist: inline_call
 //
 // Inlines a function or method body creating a `let` statement per parameter unless the parameter
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_type_alias.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_type_alias.rs
index 9adf6381c1c..353d467ed19 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_type_alias.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_type_alias.rs
@@ -3,7 +3,10 @@
 // - Remove unused aliases if there are no longer any users, see inline_call.rs.
 
 use hir::{HasSource, PathResolution};
-use ide_db::{defs::Definition, search::FileReference};
+use ide_db::{
+    defs::Definition, imports::insert_use::ast_to_remove_for_path_in_use_stmt,
+    search::FileReference,
+};
 use itertools::Itertools;
 use std::collections::HashMap;
 use syntax::{
@@ -16,6 +19,8 @@ use crate::{
     AssistId, AssistKind,
 };
 
+use super::inline_call::split_refs_and_uses;
+
 // Assist: inline_type_alias_uses
 //
 // Inline a type alias into all of its uses where possible.
@@ -31,7 +36,7 @@ use crate::{
 // ```
 // ->
 // ```
-// type A = i32;
+//
 // fn id(x: i32) -> i32 {
 //     x
 // };
@@ -58,20 +63,20 @@ pub(crate) fn inline_type_alias_uses(acc: &mut Assists, ctx: &AssistContext<'_>)
         name.syntax().text_range(),
         |builder| {
             let usages = usages.all();
+            let mut definition_deleted = false;
 
             let mut inline_refs_for_file = |file_id, refs: Vec<FileReference>| {
                 builder.edit_file(file_id);
 
-                let path_types: Vec<ast::PathType> = refs
-                    .into_iter()
-                    .filter_map(|file_ref| match file_ref.name {
-                        ast::NameLike::NameRef(path_type) => {
-                            path_type.syntax().ancestors().nth(3).and_then(ast::PathType::cast)
-                        }
-                        _ => None,
-                    })
-                    .collect();
+                let (path_types, path_type_uses) =
+                    split_refs_and_uses(builder, refs, |path_type| {
+                        path_type.syntax().ancestors().nth(3).and_then(ast::PathType::cast)
+                    });
 
+                path_type_uses
+                    .iter()
+                    .flat_map(ast_to_remove_for_path_in_use_stmt)
+                    .for_each(|x| builder.delete(x.syntax().text_range()));
                 for (target, replacement) in path_types.into_iter().filter_map(|path_type| {
                     let replacement = inline(&ast_alias, &path_type)?.to_text(&concrete_type);
                     let target = path_type.syntax().text_range();
@@ -79,11 +84,20 @@ pub(crate) fn inline_type_alias_uses(acc: &mut Assists, ctx: &AssistContext<'_>)
                 }) {
                     builder.replace(target, replacement);
                 }
+
+                if file_id == ctx.file_id() {
+                    builder.delete(ast_alias.syntax().text_range());
+                    definition_deleted = true;
+                }
             };
 
             for (file_id, refs) in usages.into_iter() {
                 inline_refs_for_file(file_id, refs);
             }
+            if !definition_deleted {
+                builder.edit_file(ctx.file_id());
+                builder.delete(ast_alias.syntax().text_range());
+            }
         },
     )
 }
@@ -929,7 +943,7 @@ fn foo() {
 }
 "#,
                 r#"
-type A = u32;
+
 
 fn foo() {
     let _: u32 = 3;
@@ -960,13 +974,13 @@ fn foo() {
                 r#"
 //- /lib.rs
 mod foo;
-type T<E> = Vec<E>;
+
 fn f() -> Vec<&str> {
     vec!["hello"]
 }
 
 //- /foo.rs
-use super::T;
+
 fn foo() {
     let _: Vec<i8> = Vec::new();
 }
@@ -990,7 +1004,12 @@ fn foo() {
 }
 "#,
                 r#"
-use super::I;
+//- /lib.rs
+mod foo;
+
+
+//- /foo.rs
+
 fn foo() {
     let _: i32 = 0;
 }
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_imports.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_imports.rs
index 7e102ceba89..2bdbec93b1f 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_imports.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_imports.rs
@@ -1,6 +1,10 @@
 use either::Either;
 use ide_db::imports::merge_imports::{try_merge_imports, try_merge_trees, MergeBehavior};
-use syntax::{algo::neighbor, ast, match_ast, ted, AstNode, SyntaxElement, SyntaxNode};
+use syntax::{
+    algo::neighbor,
+    ast::{self, edit_in_place::Removable},
+    match_ast, ted, AstNode, SyntaxElement, SyntaxNode,
+};
 
 use crate::{
     assist_context::{AssistContext, Assists},
@@ -76,7 +80,7 @@ pub(crate) fn merge_imports(acc: &mut Assists, ctx: &AssistContext<'_>) -> Optio
                 .collect();
             for edit in edits_mut {
                 match edit {
-                    Remove(it) => it.as_ref().either(ast::Use::remove, ast::UseTree::remove),
+                    Remove(it) => it.as_ref().either(Removable::remove, Removable::remove),
                     Replace(old, new) => ted::replace(old, new),
                 }
             }
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_bounds.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_bounds.rs
index 176a3bf5803..1dd376ac3fd 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_bounds.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_bounds.rs
@@ -1,5 +1,9 @@
 use syntax::{
-    ast::{self, edit_in_place::GenericParamsOwnerEdit, make, AstNode, HasName, HasTypeBounds},
+    ast::{
+        self,
+        edit_in_place::{GenericParamsOwnerEdit, Removable},
+        make, AstNode, HasName, HasTypeBounds,
+    },
     match_ast,
 };
 
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_use.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_use.rs
index 3ce028e9306..dac216b69b7 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_use.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_use.rs
@@ -1,5 +1,5 @@
 use syntax::{
-    ast::{self, make, HasVisibility},
+    ast::{self, edit_in_place::Removable, make, HasVisibility},
     ted::{self, Position},
     AstNode, SyntaxKind,
 };
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs
index a8c8622c1c1..227e2300f92 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs
@@ -1390,7 +1390,7 @@ fn foo() {
 }
 "#####,
         r#####"
-type A = i32;
+
 fn id(x: i32) -> i32 {
     x
 };
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs
index 103e3259fa2..4ab6e2627fa 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs
@@ -12,7 +12,7 @@ use syntax::{
     ast::{
         self,
         edit::{self, AstNodeEdit},
-        edit_in_place::AttrsOwnerEdit,
+        edit_in_place::{AttrsOwnerEdit, Removable},
         make, HasArgList, HasAttrs, HasGenericParams, HasName, HasTypeBounds, Whitespace,
     },
     ted, AstNode, AstToken, Direction, SmolStr, SourceFile,
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/mod_.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/mod_.rs
index 9c975b92953..950731eb4ca 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/mod_.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/mod_.rs
@@ -53,6 +53,7 @@ pub(crate) fn complete_mod(
     let existing_mod_declarations = current_module
         .children(ctx.db)
         .filter_map(|module| Some(module.name(ctx.db)?.to_string()))
+        .filter(|module| module != ctx.original_token.text())
         .collect::<FxHashSet<_>>();
 
     let module_declaration_file =
@@ -351,4 +352,23 @@ fn ignored_bar() {}
             "#]],
         );
     }
+
+    #[test]
+    fn semi_colon_completion() {
+        check(
+            r#"
+//- /lib.rs
+mod foo;
+//- /foo.rs
+mod bar {
+    mod baz$0
+}
+//- /foo/bar/baz.rs
+fn baz() {}
+"#,
+            expect![[r#"
+                md baz;
+            "#]],
+        );
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs
index 38e24ebc732..8e26d889f9b 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs
@@ -672,6 +672,45 @@ fn main() {
 }
 
 #[test]
+fn varaiant_with_struct() {
+    check_empty(
+        r#"
+pub struct YoloVariant {
+    pub f: usize
+}
+
+pub enum HH {
+    Yolo(YoloVariant),
+}
+
+fn brr() {
+    let t = HH::Yolo(Y$0);
+}
+"#,
+        expect![[r#"
+            en HH
+            fn brr()           fn()
+            st YoloVariant
+            st YoloVariant {…} YoloVariant { f: usize }
+            bt u32
+            kw crate::
+            kw false
+            kw for
+            kw if
+            kw if let
+            kw loop
+            kw match
+            kw return
+            kw self::
+            kw true
+            kw unsafe
+            kw while
+            kw while let
+        "#]],
+    );
+}
+
+#[test]
 fn return_unit_block() {
     cov_mark::check!(return_unit_block);
     check_edit("return", r#"fn f() { if true { $0 } }"#, r#"fn f() { if true { return; } }"#);
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/active_parameter.rs b/src/tools/rust-analyzer/crates/ide-db/src/active_parameter.rs
index 7303ef8b7bb..7109c6fd188 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/active_parameter.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/active_parameter.rs
@@ -12,7 +12,7 @@ use crate::RootDatabase;
 #[derive(Debug)]
 pub struct ActiveParameter {
     pub ty: Type,
-    pub pat: Either<ast::SelfParam, ast::Pat>,
+    pub pat: Option<Either<ast::SelfParam, ast::Pat>>,
 }
 
 impl ActiveParameter {
@@ -27,12 +27,12 @@ impl ActiveParameter {
             return None;
         }
         let (pat, ty) = params.swap_remove(idx);
-        pat.map(|pat| ActiveParameter { ty, pat })
+        Some(ActiveParameter { ty, pat })
     }
 
     pub fn ident(&self) -> Option<ast::Name> {
-        self.pat.as_ref().right().and_then(|param| match param {
-            ast::Pat::IdentPat(ident) => ident.name(),
+        self.pat.as_ref().and_then(|param| match param {
+            Either::Right(ast::Pat::IdentPat(ident)) => ident.name(),
             _ => None,
         })
     }
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs
index c14182279d0..9be1d366349 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs
@@ -7,7 +7,10 @@ use std::cmp::Ordering;
 use hir::Semantics;
 use syntax::{
     algo,
-    ast::{self, make, AstNode, HasAttrs, HasModuleItem, HasVisibility, PathSegmentKind},
+    ast::{
+        self, edit_in_place::Removable, make, AstNode, HasAttrs, HasModuleItem, HasVisibility,
+        PathSegmentKind,
+    },
     ted, Direction, NodeOrToken, SyntaxKind, SyntaxNode,
 };
 
@@ -192,20 +195,24 @@ pub fn insert_use(scope: &ImportScope, path: ast::Path, cfg: &InsertUseConfig) {
     insert_use_(scope, &path, cfg.group, use_item);
 }
 
-pub fn remove_path_if_in_use_stmt(path: &ast::Path) {
+pub fn ast_to_remove_for_path_in_use_stmt(path: &ast::Path) -> Option<Box<dyn Removable>> {
     // FIXME: improve this
     if path.parent_path().is_some() {
-        return;
+        return None;
     }
-    if let Some(use_tree) = path.syntax().parent().and_then(ast::UseTree::cast) {
-        if use_tree.use_tree_list().is_some() || use_tree.star_token().is_some() {
-            return;
-        }
-        if let Some(use_) = use_tree.syntax().parent().and_then(ast::Use::cast) {
-            use_.remove();
-            return;
-        }
-        use_tree.remove();
+    let use_tree = path.syntax().parent().and_then(ast::UseTree::cast)?;
+    if use_tree.use_tree_list().is_some() || use_tree.star_token().is_some() {
+        return None;
+    }
+    if let Some(use_) = use_tree.syntax().parent().and_then(ast::Use::cast) {
+        return Some(Box::new(use_));
+    }
+    Some(Box::new(use_tree))
+}
+
+pub fn remove_path_if_in_use_stmt(path: &ast::Path) {
+    if let Some(node) = ast_to_remove_for_path_in_use_stmt(path) {
+        node.remove();
     }
 }
 
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/insert_whitespace_into_node.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/insert_whitespace_into_node.rs
index f54ae6c9202..8bc093a85a2 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/insert_whitespace_into_node.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/insert_whitespace_into_node.rs
@@ -95,7 +95,7 @@ pub fn insert_ws_into(syn: SyntaxNode) -> SyntaxNode {
             AS_KW | DYN_KW | IMPL_KW | CONST_KW => {
                 mods.push(do_ws(after, tok));
             }
-            T![;] => {
+            T![;] if is_next(|it| it != R_CURLY, true) => {
                 if indent > 0 {
                     mods.push(do_indent(after, tok, indent));
                 }
diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs
index d52adaee535..c5c50d88dd2 100644
--- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs
@@ -2,12 +2,13 @@
 use std::fmt::Display;
 
 use either::Either;
-use hir::{AsAssocItem, AttributeTemplate, HasAttrs, HirDisplay, Semantics, TypeInfo};
+use hir::{AsAssocItem, AttributeTemplate, HasAttrs, HasSource, HirDisplay, Semantics, TypeInfo};
 use ide_db::{
     base_db::SourceDatabase,
     defs::Definition,
     famous_defs::FamousDefs,
     generated::lints::{CLIPPY_LINTS, DEFAULT_LINTS, FEATURES},
+    syntax_helpers::insert_whitespace_into_node,
     RootDatabase,
 };
 use itertools::Itertools;
@@ -350,10 +351,24 @@ pub(super) fn definition(
             let body = it.eval(db);
             match body {
                 Ok(x) => Some(format!("{}", x)),
-                Err(_) => it.value(db).map(|x| format!("{}", x)),
+                Err(_) => {
+                    let source = it.source(db)?;
+                    let mut body = source.value.body()?.syntax().clone();
+                    if source.file_id.is_macro() {
+                        body = insert_whitespace_into_node::insert_ws_into(body);
+                    }
+                    Some(body.to_string())
+                }
+            }
+        }),
+        Definition::Static(it) => label_value_and_docs(db, it, |it| {
+            let source = it.source(db)?;
+            let mut body = source.value.body()?.syntax().clone();
+            if source.file_id.is_macro() {
+                body = insert_whitespace_into_node::insert_ws_into(body);
             }
+            Some(body.to_string())
         }),
-        Definition::Static(it) => label_value_and_docs(db, it, |it| it.value(db)),
         Definition::Trait(it) => label_and_docs(db, it),
         Definition::TypeAlias(it) => label_and_docs(db, it),
         Definition::BuiltinType(it) => {
diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
index 685eb4521eb..4b8b47783d1 100644
--- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
@@ -5113,3 +5113,61 @@ fn f() {
         "#]],
     );
 }
+
+#[test]
+fn static_const_macro_expanded_body() {
+    check(
+        r#"
+macro_rules! m {
+    () => {
+        pub const V: i8 = {
+            let e = 123;
+            f(e) // Prevent const eval from evaluating this constant, we want to print the body's code.
+        };
+    };
+}
+m!();
+fn main() { $0V; }
+"#,
+        expect![[r#"
+            *V*
+
+            ```rust
+            test
+            ```
+
+            ```rust
+            pub const V: i8 = {
+              let e = 123;
+              f(e)
+            }
+            ```
+        "#]],
+    );
+    check(
+        r#"
+macro_rules! m {
+    () => {
+        pub static V: i8 = {
+            let e = 123;
+        };
+    };
+}
+m!();
+fn main() { $0V; }
+"#,
+        expect![[r#"
+            *V*
+
+            ```rust
+            test
+            ```
+
+            ```rust
+            pub static V: i8 = {
+              let e = 123;
+            }
+            ```
+        "#]],
+    );
+}
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs
index e9034daefa8..d1b1d2c331a 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs
@@ -1,3 +1,5 @@
+use std::fmt;
+
 use either::Either;
 use hir::{known, Callable, HasVisibility, HirDisplay, Mutability, Semantics, TypeInfo};
 use ide_db::{
@@ -69,7 +71,7 @@ pub enum InlayKind {
 pub struct InlayHint {
     pub range: TextRange,
     pub kind: InlayKind,
-    pub label: String,
+    pub label: InlayHintLabel,
     pub tooltip: Option<InlayTooltip>,
 }
 
@@ -80,6 +82,83 @@ pub enum InlayTooltip {
     HoverOffset(FileId, TextSize),
 }
 
+pub struct InlayHintLabel {
+    pub parts: Vec<InlayHintLabelPart>,
+}
+
+impl InlayHintLabel {
+    pub fn as_simple_str(&self) -> Option<&str> {
+        match &*self.parts {
+            [part] => part.as_simple_str(),
+            _ => None,
+        }
+    }
+
+    pub fn prepend_str(&mut self, s: &str) {
+        match &mut *self.parts {
+            [part, ..] if part.as_simple_str().is_some() => part.text = format!("{s}{}", part.text),
+            _ => self.parts.insert(0, InlayHintLabelPart { text: s.into(), linked_location: None }),
+        }
+    }
+
+    pub fn append_str(&mut self, s: &str) {
+        match &mut *self.parts {
+            [.., part] if part.as_simple_str().is_some() => part.text.push_str(s),
+            _ => self.parts.push(InlayHintLabelPart { text: s.into(), linked_location: None }),
+        }
+    }
+}
+
+impl From<String> for InlayHintLabel {
+    fn from(s: String) -> Self {
+        Self { parts: vec![InlayHintLabelPart { text: s, linked_location: None }] }
+    }
+}
+
+impl fmt::Display for InlayHintLabel {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "{}", self.parts.iter().map(|part| &part.text).format(""))
+    }
+}
+
+impl fmt::Debug for InlayHintLabel {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.debug_list().entries(&self.parts).finish()
+    }
+}
+
+pub struct InlayHintLabelPart {
+    pub text: String,
+    /// Source location represented by this label part. The client will use this to fetch the part's
+    /// hover tooltip, and Ctrl+Clicking the label part will navigate to the definition the location
+    /// refers to (not necessarily the location itself).
+    /// When setting this, no tooltip must be set on the containing hint, or VS Code will display
+    /// them both.
+    pub linked_location: Option<FileRange>,
+}
+
+impl InlayHintLabelPart {
+    pub fn as_simple_str(&self) -> Option<&str> {
+        match self {
+            Self { text, linked_location: None } => Some(text),
+            _ => None,
+        }
+    }
+}
+
+impl fmt::Debug for InlayHintLabelPart {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match self.as_simple_str() {
+            Some(string) => string.fmt(f),
+            None => f
+                .debug_struct("InlayHintLabelPart")
+                .field("text", &self.text)
+                .field("linked_location", &self.linked_location)
+                .finish(),
+        }
+    }
+}
+
 // Feature: Inlay Hints
 //
 // rust-analyzer shows additional information inline with the source code.
@@ -192,10 +271,10 @@ fn closing_brace_hints(
 ) -> Option<()> {
     let min_lines = config.closing_brace_hints_min_lines?;
 
-    let name = |it: ast::Name| it.syntax().text_range().start();
+    let name = |it: ast::Name| it.syntax().text_range();
 
     let mut closing_token;
-    let (label, name_offset) = if let Some(item_list) = ast::AssocItemList::cast(node.clone()) {
+    let (label, name_range) = if let Some(item_list) = ast::AssocItemList::cast(node.clone()) {
         closing_token = item_list.r_curly_token()?;
 
         let parent = item_list.syntax().parent()?;
@@ -205,11 +284,11 @@ fn closing_brace_hints(
                     let imp = sema.to_def(&imp)?;
                     let ty = imp.self_ty(sema.db);
                     let trait_ = imp.trait_(sema.db);
-
-                    (match trait_ {
+                    let hint_text = match trait_ {
                         Some(tr) => format!("impl {} for {}", tr.name(sema.db), ty.display_truncated(sema.db, config.max_length)),
                         None => format!("impl {}", ty.display_truncated(sema.db, config.max_length)),
-                    }, None)
+                    };
+                    (hint_text, None)
                 },
                 ast::Trait(tr) => {
                     (format!("trait {}", tr.name()?), tr.name().map(name))
@@ -253,7 +332,7 @@ fn closing_brace_hints(
 
         (
             format!("{}!", mac.path()?),
-            mac.path().and_then(|it| it.segment()).map(|it| it.syntax().text_range().start()),
+            mac.path().and_then(|it| it.segment()).map(|it| it.syntax().text_range()),
         )
     } else {
         return None;
@@ -278,11 +357,12 @@ fn closing_brace_hints(
         return None;
     }
 
+    let linked_location = name_range.map(|range| FileRange { file_id, range });
     acc.push(InlayHint {
         range: closing_token.text_range(),
         kind: InlayKind::ClosingBraceHint,
-        label,
-        tooltip: name_offset.map(|it| InlayTooltip::HoverOffset(file_id, it)),
+        label: InlayHintLabel { parts: vec![InlayHintLabelPart { text: label, linked_location }] },
+        tooltip: None, // provided by label part location
     });
 
     None
@@ -311,7 +391,7 @@ fn implicit_static_hints(
             acc.push(InlayHint {
                 range: t.text_range(),
                 kind: InlayKind::LifetimeHint,
-                label: "'static".to_owned(),
+                label: "'static".to_owned().into(),
                 tooltip: Some(InlayTooltip::String("Elided static lifetime".into())),
             });
         }
@@ -329,10 +409,10 @@ fn fn_lifetime_fn_hints(
         return None;
     }
 
-    let mk_lt_hint = |t: SyntaxToken, label| InlayHint {
+    let mk_lt_hint = |t: SyntaxToken, label: String| InlayHint {
         range: t.text_range(),
         kind: InlayKind::LifetimeHint,
-        label,
+        label: label.into(),
         tooltip: Some(InlayTooltip::String("Elided lifetime".into())),
     };
 
@@ -486,7 +566,8 @@ fn fn_lifetime_fn_hints(
                     "{}{}",
                     allocated_lifetimes.iter().format(", "),
                     if is_empty { "" } else { ", " }
-                ),
+                )
+                .into(),
                 tooltip: Some(InlayTooltip::String("Elided lifetimes".into())),
             });
         }
@@ -535,7 +616,8 @@ fn closure_ret_hints(
         range: param_list.syntax().text_range(),
         kind: InlayKind::ClosureReturnTypeHint,
         label: hint_iterator(sema, &famous_defs, config, &ty)
-            .unwrap_or_else(|| ty.display_truncated(sema.db, config.max_length).to_string()),
+            .unwrap_or_else(|| ty.display_truncated(sema.db, config.max_length).to_string())
+            .into(),
         tooltip: Some(InlayTooltip::HoverRanged(file_id, param_list.syntax().text_range())),
     });
     Some(())
@@ -562,7 +644,7 @@ fn reborrow_hints(
     acc.push(InlayHint {
         range: expr.syntax().text_range(),
         kind: InlayKind::ImplicitReborrowHint,
-        label: label.to_string(),
+        label: label.to_string().into(),
         tooltip: Some(InlayTooltip::String("Compiler inserted reborrow".into())),
     });
     Some(())
@@ -620,9 +702,9 @@ fn chaining_hints(
             acc.push(InlayHint {
                 range: expr.syntax().text_range(),
                 kind: InlayKind::ChainingHint,
-                label: hint_iterator(sema, &famous_defs, config, &ty).unwrap_or_else(|| {
-                    ty.display_truncated(sema.db, config.max_length).to_string()
-                }),
+                label: hint_iterator(sema, &famous_defs, config, &ty)
+                    .unwrap_or_else(|| ty.display_truncated(sema.db, config.max_length).to_string())
+                    .into(),
                 tooltip: Some(InlayTooltip::HoverRanged(file_id, expr.syntax().text_range())),
             });
         }
@@ -674,7 +756,7 @@ fn param_name_hints(
             InlayHint {
                 range,
                 kind: InlayKind::ParameterHint,
-                label: param_name,
+                label: param_name.into(),
                 tooltip: tooltip.map(|it| InlayTooltip::HoverOffset(it.file_id, it.range.start())),
             }
         });
@@ -705,7 +787,7 @@ fn binding_mode_hints(
         acc.push(InlayHint {
             range,
             kind: InlayKind::BindingModeHint,
-            label: r.to_string(),
+            label: r.to_string().into(),
             tooltip: Some(InlayTooltip::String("Inferred binding mode".into())),
         });
     });
@@ -720,7 +802,7 @@ fn binding_mode_hints(
             acc.push(InlayHint {
                 range,
                 kind: InlayKind::BindingModeHint,
-                label: bm.to_string(),
+                label: bm.to_string().into(),
                 tooltip: Some(InlayTooltip::String("Inferred binding mode".into())),
             });
         }
@@ -772,7 +854,7 @@ fn bind_pat_hints(
             None => pat.syntax().text_range(),
         },
         kind: InlayKind::TypeHint,
-        label,
+        label: label.into(),
         tooltip: pat
             .name()
             .map(|it| it.syntax().text_range())
@@ -2223,7 +2305,9 @@ fn main() {
                     InlayHint {
                         range: 147..172,
                         kind: ChainingHint,
-                        label: "B",
+                        label: [
+                            "B",
+                        ],
                         tooltip: Some(
                             HoverRanged(
                                 FileId(
@@ -2236,7 +2320,9 @@ fn main() {
                     InlayHint {
                         range: 147..154,
                         kind: ChainingHint,
-                        label: "A",
+                        label: [
+                            "A",
+                        ],
                         tooltip: Some(
                             HoverRanged(
                                 FileId(
@@ -2294,7 +2380,9 @@ fn main() {
                     InlayHint {
                         range: 143..190,
                         kind: ChainingHint,
-                        label: "C",
+                        label: [
+                            "C",
+                        ],
                         tooltip: Some(
                             HoverRanged(
                                 FileId(
@@ -2307,7 +2395,9 @@ fn main() {
                     InlayHint {
                         range: 143..179,
                         kind: ChainingHint,
-                        label: "B",
+                        label: [
+                            "B",
+                        ],
                         tooltip: Some(
                             HoverRanged(
                                 FileId(
@@ -2350,7 +2440,9 @@ fn main() {
                     InlayHint {
                         range: 246..283,
                         kind: ChainingHint,
-                        label: "B<X<i32, bool>>",
+                        label: [
+                            "B<X<i32, bool>>",
+                        ],
                         tooltip: Some(
                             HoverRanged(
                                 FileId(
@@ -2363,7 +2455,9 @@ fn main() {
                     InlayHint {
                         range: 246..265,
                         kind: ChainingHint,
-                        label: "A<X<i32, bool>>",
+                        label: [
+                            "A<X<i32, bool>>",
+                        ],
                         tooltip: Some(
                             HoverRanged(
                                 FileId(
@@ -2408,7 +2502,9 @@ fn main() {
                     InlayHint {
                         range: 174..241,
                         kind: ChainingHint,
-                        label: "impl Iterator<Item = ()>",
+                        label: [
+                            "impl Iterator<Item = ()>",
+                        ],
                         tooltip: Some(
                             HoverRanged(
                                 FileId(
@@ -2421,7 +2517,9 @@ fn main() {
                     InlayHint {
                         range: 174..224,
                         kind: ChainingHint,
-                        label: "impl Iterator<Item = ()>",
+                        label: [
+                            "impl Iterator<Item = ()>",
+                        ],
                         tooltip: Some(
                             HoverRanged(
                                 FileId(
@@ -2434,7 +2532,9 @@ fn main() {
                     InlayHint {
                         range: 174..206,
                         kind: ChainingHint,
-                        label: "impl Iterator<Item = ()>",
+                        label: [
+                            "impl Iterator<Item = ()>",
+                        ],
                         tooltip: Some(
                             HoverRanged(
                                 FileId(
@@ -2447,7 +2547,9 @@ fn main() {
                     InlayHint {
                         range: 174..189,
                         kind: ChainingHint,
-                        label: "&mut MyIter",
+                        label: [
+                            "&mut MyIter",
+                        ],
                         tooltip: Some(
                             HoverRanged(
                                 FileId(
@@ -2489,7 +2591,9 @@ fn main() {
                     InlayHint {
                         range: 124..130,
                         kind: TypeHint,
-                        label: "Struct",
+                        label: [
+                            "Struct",
+                        ],
                         tooltip: Some(
                             HoverRanged(
                                 FileId(
@@ -2502,7 +2606,9 @@ fn main() {
                     InlayHint {
                         range: 145..185,
                         kind: ChainingHint,
-                        label: "Struct",
+                        label: [
+                            "Struct",
+                        ],
                         tooltip: Some(
                             HoverRanged(
                                 FileId(
@@ -2515,7 +2621,9 @@ fn main() {
                     InlayHint {
                         range: 145..168,
                         kind: ChainingHint,
-                        label: "Struct",
+                        label: [
+                            "Struct",
+                        ],
                         tooltip: Some(
                             HoverRanged(
                                 FileId(
@@ -2528,7 +2636,9 @@ fn main() {
                     InlayHint {
                         range: 222..228,
                         kind: ParameterHint,
-                        label: "self",
+                        label: [
+                            "self",
+                        ],
                         tooltip: Some(
                             HoverOffset(
                                 FileId(
diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs
index d61d69a090b..0552330814a 100644
--- a/src/tools/rust-analyzer/crates/ide/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs
@@ -82,8 +82,8 @@ pub use crate::{
     highlight_related::{HighlightRelatedConfig, HighlightedRange},
     hover::{HoverAction, HoverConfig, HoverDocFormat, HoverGotoTypeData, HoverResult},
     inlay_hints::{
-        ClosureReturnTypeHints, InlayHint, InlayHintsConfig, InlayKind, InlayTooltip,
-        LifetimeElisionHints, ReborrowHints,
+        ClosureReturnTypeHints, InlayHint, InlayHintLabel, InlayHintsConfig, InlayKind,
+        InlayTooltip, LifetimeElisionHints, ReborrowHints,
     },
     join_lines::JoinLinesConfig,
     markup::Markup,
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers.rs
index d9b669afbe8..e79cf3d3fd6 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers.rs
@@ -1362,7 +1362,7 @@ pub(crate) fn handle_inlay_hints(
             .map(|it| {
                 to_proto::inlay_hint(&snap, &line_index, inlay_hints_config.render_colons, it)
             })
-            .collect(),
+            .collect::<Result<Vec<_>>>()?,
     ))
 }
 
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
index f23bbca6387..e47f70fff39 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
@@ -314,7 +314,9 @@ impl GlobalState {
                         let mut args = args.clone();
                         let mut path = path.clone();
 
-                        if let ProjectWorkspace::Cargo { sysroot, .. } = ws {
+                        if let ProjectWorkspace::Cargo { sysroot, .. }
+                        | ProjectWorkspace::Json { sysroot, .. } = ws
+                        {
                             tracing::debug!("Found a cargo workspace...");
                             if let Some(sysroot) = sysroot.as_ref() {
                                 tracing::debug!("Found a cargo workspace with a sysroot...");
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/to_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/to_proto.rs
index 102cd602950..e083b9d0e33 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/to_proto.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/to_proto.rs
@@ -9,8 +9,9 @@ use ide::{
     Annotation, AnnotationKind, Assist, AssistKind, Cancellable, CompletionItem,
     CompletionItemKind, CompletionRelevance, Documentation, FileId, FileRange, FileSystemEdit,
     Fold, FoldKind, Highlight, HlMod, HlOperator, HlPunct, HlRange, HlTag, Indel, InlayHint,
-    InlayKind, Markup, NavigationTarget, ReferenceCategory, RenameError, Runnable, Severity,
-    SignatureHelp, SourceChange, StructureNodeKind, SymbolKind, TextEdit, TextRange, TextSize,
+    InlayHintLabel, InlayKind, Markup, NavigationTarget, ReferenceCategory, RenameError, Runnable,
+    Severity, SignatureHelp, SourceChange, StructureNodeKind, SymbolKind, TextEdit, TextRange,
+    TextSize,
 };
 use itertools::Itertools;
 use serde_json::to_value;
@@ -426,9 +427,16 @@ pub(crate) fn inlay_hint(
     snap: &GlobalStateSnapshot,
     line_index: &LineIndex,
     render_colons: bool,
-    inlay_hint: InlayHint,
-) -> lsp_types::InlayHint {
-    lsp_types::InlayHint {
+    mut inlay_hint: InlayHint,
+) -> Result<lsp_types::InlayHint> {
+    match inlay_hint.kind {
+        InlayKind::ParameterHint if render_colons => inlay_hint.label.append_str(":"),
+        InlayKind::TypeHint if render_colons => inlay_hint.label.prepend_str(": "),
+        InlayKind::ClosureReturnTypeHint => inlay_hint.label.prepend_str(" -> "),
+        _ => {}
+    }
+
+    Ok(lsp_types::InlayHint {
         position: match inlay_hint.kind {
             // before annotated thing
             InlayKind::ParameterHint
@@ -459,15 +467,9 @@ pub(crate) fn inlay_hint(
             | InlayKind::ImplicitReborrowHint
             | InlayKind::TypeHint
             | InlayKind::ClosingBraceHint => false,
-            InlayKind::BindingModeHint => inlay_hint.label != "&",
+            InlayKind::BindingModeHint => inlay_hint.label.as_simple_str() != Some("&"),
             InlayKind::ParameterHint | InlayKind::LifetimeHint => true,
         }),
-        label: lsp_types::InlayHintLabel::String(match inlay_hint.kind {
-            InlayKind::ParameterHint if render_colons => format!("{}:", inlay_hint.label),
-            InlayKind::TypeHint if render_colons => format!(": {}", inlay_hint.label),
-            InlayKind::ClosureReturnTypeHint => format!(" -> {}", inlay_hint.label),
-            _ => inlay_hint.label.clone(),
-        }),
         kind: match inlay_hint.kind {
             InlayKind::ParameterHint => Some(lsp_types::InlayHintKind::PARAMETER),
             InlayKind::ClosureReturnTypeHint | InlayKind::TypeHint | InlayKind::ChainingHint => {
@@ -506,9 +508,36 @@ pub(crate) fn inlay_hint(
         })(),
         tooltip: Some(match inlay_hint.tooltip {
             Some(ide::InlayTooltip::String(s)) => lsp_types::InlayHintTooltip::String(s),
-            _ => lsp_types::InlayHintTooltip::String(inlay_hint.label),
+            _ => lsp_types::InlayHintTooltip::String(inlay_hint.label.to_string()),
         }),
-    }
+        label: inlay_hint_label(snap, inlay_hint.label)?,
+    })
+}
+
+fn inlay_hint_label(
+    snap: &GlobalStateSnapshot,
+    label: InlayHintLabel,
+) -> Result<lsp_types::InlayHintLabel> {
+    Ok(match label.as_simple_str() {
+        Some(s) => lsp_types::InlayHintLabel::String(s.into()),
+        None => lsp_types::InlayHintLabel::LabelParts(
+            label
+                .parts
+                .into_iter()
+                .map(|part| {
+                    Ok(lsp_types::InlayHintLabelPart {
+                        value: part.text,
+                        tooltip: None,
+                        location: part
+                            .linked_location
+                            .map(|range| location(snap, range))
+                            .transpose()?,
+                        command: None,
+                    })
+                })
+                .collect::<Result<Vec<_>>>()?,
+        ),
+    })
 }
 
 static TOKEN_RESULT_COUNTER: AtomicU32 = AtomicU32::new(1);
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs
index 8efd58e2c39..eadebbe8a21 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs
@@ -248,8 +248,12 @@ impl ast::WhereClause {
     }
 }
 
-impl ast::TypeBoundList {
-    pub fn remove(&self) {
+pub trait Removable: AstNode {
+    fn remove(&self);
+}
+
+impl Removable for ast::TypeBoundList {
+    fn remove(&self) {
         match self.syntax().siblings_with_tokens(Direction::Prev).find(|it| it.kind() == T![:]) {
             Some(colon) => ted::remove_all(colon..=self.syntax().clone().into()),
             None => ted::remove(self.syntax()),
@@ -267,8 +271,8 @@ impl ast::PathSegment {
     }
 }
 
-impl ast::UseTree {
-    pub fn remove(&self) {
+impl Removable for ast::UseTree {
+    fn remove(&self) {
         for dir in [Direction::Next, Direction::Prev] {
             if let Some(next_use_tree) = neighbor(self, dir) {
                 let separators = self
@@ -282,7 +286,9 @@ impl ast::UseTree {
         }
         ted::remove(self.syntax());
     }
+}
 
+impl ast::UseTree {
     pub fn get_or_create_use_tree_list(&self) -> ast::UseTreeList {
         match self.use_tree_list() {
             Some(it) => it,
@@ -373,8 +379,8 @@ impl ast::UseTreeList {
     }
 }
 
-impl ast::Use {
-    pub fn remove(&self) {
+impl Removable for ast::Use {
+    fn remove(&self) {
         let next_ws = self
             .syntax()
             .next_sibling_or_token()
@@ -444,8 +450,8 @@ impl ast::Fn {
     }
 }
 
-impl ast::MatchArm {
-    pub fn remove(&self) {
+impl Removable for ast::MatchArm {
+    fn remove(&self) {
         if let Some(sibling) = self.syntax().prev_sibling_or_token() {
             if sibling.kind() == SyntaxKind::WHITESPACE {
                 ted::remove(sibling);