about summary refs log tree commit diff
path: root/src/tools/rust-analyzer/crates/ide
diff options
context:
space:
mode:
authorLaurențiu Nicola <lnicola@dend.ro>2023-06-05 12:04:23 +0300
committerLaurențiu Nicola <lnicola@dend.ro>2023-06-05 12:04:23 +0300
commitb8a7d439db0cfd765ed4bfedd2bbaeeee58b05a5 (patch)
tree5adcbc6cf50af3bebc2cd4f42d5252a4d728690e /src/tools/rust-analyzer/crates/ide
parent51f714c8c5021fe25442e46798b1cbef2f2249ed (diff)
parentaa9bc8612514d216f84eec218dfd19ab83f3598a (diff)
downloadrust-b8a7d439db0cfd765ed4bfedd2bbaeeee58b05a5.tar.gz
rust-b8a7d439db0cfd765ed4bfedd2bbaeeee58b05a5.zip
Merge commit 'aa9bc8612514d216f84eec218dfd19ab83f3598a' into sync-from-ra
Diffstat (limited to 'src/tools/rust-analyzer/crates/ide')
-rw-r--r--src/tools/rust-analyzer/crates/ide/Cargo.toml2
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs8
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/doc_links.rs177
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs152
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/expand_macro.rs85
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/extend_selection.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/fetch_crates.rs42
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/file_structure.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/fixture.rs15
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/goto_definition.rs54
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs89
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/highlight_related.rs351
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/hover.rs44
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/hover/render.rs333
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/hover/tests.rs854
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs146
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs113
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs450
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs27
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs93
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs8
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs207
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_ret.rs80
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs144
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/fn_lifetime_fn.rs14
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_static.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs17
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/interpret_function.rs46
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/lib.rs59
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/moniker.rs122
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/navigation_target.rs213
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/prime_caches.rs11
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/references.rs26
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/runnables.rs46
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/shuffle_crate_graph.rs11
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/signature_help.rs502
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/ssr.rs3
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/static_index.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/status.rs241
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs160
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/escape.rs24
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs10
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs29
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs8
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html6
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html2
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html6
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html24
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords.html2
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html28
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html121
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html10
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs13
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_tree.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/view_crate_graph.rs10
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/view_item_tree.rs2
56 files changed, 4028 insertions, 1232 deletions
diff --git a/src/tools/rust-analyzer/crates/ide/Cargo.toml b/src/tools/rust-analyzer/crates/ide/Cargo.toml
index 30e514e4136..2aee203c4ea 100644
--- a/src/tools/rust-analyzer/crates/ide/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/ide/Cargo.toml
@@ -23,6 +23,8 @@ pulldown-cmark = { version = "0.9.1", default-features = false }
 url = "2.3.1"
 dot = "0.1.4"
 smallvec.workspace = true
+triomphe.workspace = true
+nohash-hasher.workspace = true
 
 # local deps
 cfg.workspace = true
diff --git a/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs b/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs
index 48bcd37b62c..dd1d0d75c63 100644
--- a/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs
@@ -263,7 +263,7 @@ mod tests {
             expect![["callee Function FileId(0) 0..14 3..9"]],
             expect![[r#"
                 caller1 Function FileId(0) 15..45 18..25 : [34..40]
-                test_caller Function FileId(0) 95..149 110..121 : [134..140]"#]],
+                test_caller Function FileId(0) 95..149 110..121 tests : [134..140]"#]],
             expect![[]],
         );
     }
@@ -283,7 +283,7 @@ fn caller() {
 //- /foo/mod.rs
 pub fn callee() {}
 "#,
-            expect![["callee Function FileId(1) 0..18 7..13"]],
+            expect!["callee Function FileId(1) 0..18 7..13 foo"],
             expect![["caller Function FileId(0) 27..56 30..36 : [45..51]"]],
             expect![[]],
         );
@@ -323,7 +323,7 @@ pub fn callee() {}
 "#,
             expect![["caller Function FileId(0) 27..56 30..36"]],
             expect![[]],
-            expect![["callee Function FileId(1) 0..18 7..13 : [45..51]"]],
+            expect!["callee Function FileId(1) 0..18 7..13 foo : [45..51]"],
         );
     }
 
@@ -477,7 +477,7 @@ fn caller() {
     S1::callee();
 }
 "#,
-            expect![["callee Function FileId(0) 15..27 18..24"]],
+            expect!["callee Function FileId(0) 15..27 18..24 T1"],
             expect![["caller Function FileId(0) 82..115 85..91 : [104..110]"]],
             expect![[]],
         );
diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs
index fae25f31023..8112c4f7259 100644
--- a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs
@@ -5,6 +5,8 @@ mod tests;
 
 mod intra_doc_links;
 
+use std::ffi::OsStr;
+
 use pulldown_cmark::{BrokenLink, CowStr, Event, InlineStr, LinkType, Options, Parser, Tag};
 use pulldown_cmark_to_cmark::{cmark_resume_with_options, Options as CMarkOptions};
 use stdx::format_to;
@@ -12,7 +14,7 @@ use url::Url;
 
 use hir::{db::HirDatabase, Adt, AsAssocItem, AssocItem, AssocItemContainer, HasAttrs};
 use ide_db::{
-    base_db::{CrateOrigin, LangCrateOrigin, SourceDatabase},
+    base_db::{CrateOrigin, LangCrateOrigin, ReleaseChannel, SourceDatabase},
     defs::{Definition, NameClass, NameRefClass},
     helpers::pick_best_token,
     RootDatabase,
@@ -29,8 +31,16 @@ use crate::{
     FilePosition, Semantics,
 };
 
-/// Weblink to an item's documentation.
-pub(crate) type DocumentationLink = String;
+/// Web and local links to an item's documentation.
+#[derive(Default, Debug, Clone, PartialEq, Eq)]
+pub struct DocumentationLinks {
+    /// The URL to the documentation on docs.rs.
+    /// May not lead anywhere.
+    pub web_url: Option<String>,
+    /// The URL to the documentation in the local file system.
+    /// May not lead anywhere.
+    pub local_url: Option<String>,
+}
 
 const MARKDOWN_OPTIONS: Options =
     Options::ENABLE_FOOTNOTES.union(Options::ENABLE_TABLES).union(Options::ENABLE_TASKLISTS);
@@ -109,7 +119,7 @@ pub(crate) fn remove_links(markdown: &str) -> String {
 
 // Feature: Open Docs
 //
-// Retrieve a link to documentation for the given symbol.
+// Retrieve a links to documentation for the given symbol.
 //
 // The simplest way to use this feature is via the context menu. Right-click on
 // the selected item. The context menu opens. Select **Open Docs**.
@@ -122,7 +132,9 @@ pub(crate) fn remove_links(markdown: &str) -> String {
 pub(crate) fn external_docs(
     db: &RootDatabase,
     position: &FilePosition,
-) -> Option<DocumentationLink> {
+    target_dir: Option<&OsStr>,
+    sysroot: Option<&OsStr>,
+) -> Option<DocumentationLinks> {
     let sema = &Semantics::new(db);
     let file = sema.parse(position.file_id).syntax().clone();
     let token = pick_best_token(file.token_at_offset(position.offset), |kind| match kind {
@@ -146,11 +158,11 @@ pub(crate) fn external_docs(
                 NameClass::Definition(it) | NameClass::ConstReference(it) => it,
                 NameClass::PatFieldShorthand { local_def: _, field_ref } => Definition::Field(field_ref),
             },
-            _ => return None,
+            _ => return None
         }
     };
 
-    get_doc_link(db, definition)
+    Some(get_doc_links(db, definition, target_dir, sysroot))
 }
 
 /// Extracts all links from a given markdown text returning the definition text range, link-text
@@ -308,19 +320,35 @@ fn broken_link_clone_cb(link: BrokenLink<'_>) -> Option<(CowStr<'_>, CowStr<'_>)
 //
 // This should cease to be a problem if RFC2988 (Stable Rustdoc URLs) is implemented
 // https://github.com/rust-lang/rfcs/pull/2988
-fn get_doc_link(db: &RootDatabase, def: Definition) -> Option<String> {
-    let (target, file, frag) = filename_and_frag_for_def(db, def)?;
+fn get_doc_links(
+    db: &RootDatabase,
+    def: Definition,
+    target_dir: Option<&OsStr>,
+    sysroot: Option<&OsStr>,
+) -> DocumentationLinks {
+    let join_url = |base_url: Option<Url>, path: &str| -> Option<Url> {
+        base_url.and_then(|url| url.join(path).ok())
+    };
+
+    let Some((target, file, frag)) = filename_and_frag_for_def(db, def) else { return Default::default(); };
 
-    let mut url = get_doc_base_url(db, target)?;
+    let (mut web_url, mut local_url) = get_doc_base_urls(db, target, target_dir, sysroot);
 
     if let Some(path) = mod_path_of_def(db, target) {
-        url = url.join(&path).ok()?;
+        web_url = join_url(web_url, &path);
+        local_url = join_url(local_url, &path);
     }
 
-    url = url.join(&file).ok()?;
-    url.set_fragment(frag.as_deref());
+    web_url = join_url(web_url, &file);
+    local_url = join_url(local_url, &file);
 
-    Some(url.into())
+    web_url.as_mut().map(|url| url.set_fragment(frag.as_deref()));
+    local_url.as_mut().map(|url| url.set_fragment(frag.as_deref()));
+
+    DocumentationLinks {
+        web_url: web_url.map(|it| it.into()),
+        local_url: local_url.map(|it| it.into()),
+    }
 }
 
 fn rewrite_intra_doc_link(
@@ -332,7 +360,7 @@ fn rewrite_intra_doc_link(
     let (link, ns) = parse_intra_doc_link(target);
 
     let resolved = resolve_doc_path_for_def(db, def, link, ns)?;
-    let mut url = get_doc_base_url(db, resolved)?;
+    let mut url = get_doc_base_urls(db, resolved, None, None).0?;
 
     let (_, file, frag) = filename_and_frag_for_def(db, resolved)?;
     if let Some(path) = mod_path_of_def(db, resolved) {
@@ -351,7 +379,7 @@ fn rewrite_url_link(db: &RootDatabase, def: Definition, target: &str) -> Option<
         return None;
     }
 
-    let mut url = get_doc_base_url(db, def)?;
+    let mut url = get_doc_base_urls(db, def, None, None).0?;
     let (def, file, frag) = filename_and_frag_for_def(db, def)?;
 
     if let Some(path) = mod_path_of_def(db, def) {
@@ -366,7 +394,7 @@ fn rewrite_url_link(db: &RootDatabase, def: Definition, target: &str) -> Option<
 fn mod_path_of_def(db: &RootDatabase, def: Definition) -> Option<String> {
     def.canonical_module_path(db).map(|it| {
         let mut path = String::new();
-        it.flat_map(|it| it.name(db)).for_each(|name| format_to!(path, "{}/", name));
+        it.flat_map(|it| it.name(db)).for_each(|name| format_to!(path, "{}/", name.display(db)));
         path
     })
 }
@@ -426,18 +454,38 @@ fn map_links<'e>(
 /// ```ignore
 /// https://doc.rust-lang.org/std/iter/trait.Iterator.html#tymethod.next
 /// ^^^^^^^^^^^^^^^^^^^^^^^^^^
+/// file:///project/root/target/doc/std/iter/trait.Iterator.html#tymethod.next
+/// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 /// ```
-fn get_doc_base_url(db: &RootDatabase, def: Definition) -> Option<Url> {
+fn get_doc_base_urls(
+    db: &RootDatabase,
+    def: Definition,
+    target_dir: Option<&OsStr>,
+    sysroot: Option<&OsStr>,
+) -> (Option<Url>, Option<Url>) {
+    let local_doc = target_dir
+        .and_then(|path| path.to_str())
+        .and_then(|path| Url::parse(&format!("file:///{path}/")).ok())
+        .and_then(|it| it.join("doc/").ok());
+    let system_doc = sysroot
+        .and_then(|it| it.to_str())
+        .map(|sysroot| format!("file:///{sysroot}/share/doc/rust/html/"))
+        .and_then(|it| Url::parse(&it).ok());
+
     // special case base url of `BuiltinType` to core
     // https://github.com/rust-lang/rust-analyzer/issues/12250
     if let Definition::BuiltinType(..) = def {
-        return Url::parse("https://doc.rust-lang.org/nightly/core/").ok();
+        let web_link = Url::parse("https://doc.rust-lang.org/nightly/core/").ok();
+        let system_link = system_doc.and_then(|it| it.join("core/").ok());
+        return (web_link, system_link);
     };
 
-    let krate = def.krate(db)?;
-    let display_name = krate.display_name(db)?;
+    let Some(krate) = def.krate(db) else { return Default::default() };
+    let Some(display_name) = krate.display_name(db) else { return Default::default() };
+    let crate_data = &db.crate_graph()[krate.into()];
+    let channel = crate_data.channel.map_or("nightly", ReleaseChannel::as_str);
 
-    let base = match db.crate_graph()[krate.into()].origin {
+    let (web_base, local_base) = match &crate_data.origin {
         // std and co do not specify `html_root_url` any longer so we gotta handwrite this ourself.
         // FIXME: Use the toolchains channel instead of nightly
         CrateOrigin::Lang(
@@ -447,10 +495,17 @@ fn get_doc_base_url(db: &RootDatabase, def: Definition) -> Option<Url> {
             | LangCrateOrigin::Std
             | LangCrateOrigin::Test),
         ) => {
-            format!("https://doc.rust-lang.org/nightly/{origin}")
+            let system_url = system_doc.and_then(|it| it.join(&format!("{origin}")).ok());
+            let web_url = format!("https://doc.rust-lang.org/{channel}/{origin}");
+            (Some(web_url), system_url)
         }
-        _ => {
-            krate.get_html_root_url(db).or_else(|| {
+        CrateOrigin::Lang(_) => return (None, None),
+        CrateOrigin::Rustc { name: _ } => {
+            (Some(format!("https://doc.rust-lang.org/{channel}/nightly-rustc/")), None)
+        }
+        CrateOrigin::Local { repo: _, name: _ } => {
+            // FIXME: These should not attempt to link to docs.rs!
+            let weblink = krate.get_html_root_url(db).or_else(|| {
                 let version = krate.version(db);
                 // Fallback to docs.rs. This uses `display_name` and can never be
                 // correct, but that's what fallbacks are about.
@@ -462,10 +517,32 @@ fn get_doc_base_url(db: &RootDatabase, def: Definition) -> Option<Url> {
                     krate = display_name,
                     version = version.as_deref().unwrap_or("*")
                 ))
-            })?
+            });
+            (weblink, local_doc)
+        }
+        CrateOrigin::Library { repo: _, name } => {
+            let weblink = krate.get_html_root_url(db).or_else(|| {
+                let version = krate.version(db);
+                // Fallback to docs.rs. This uses `display_name` and can never be
+                // correct, but that's what fallbacks are about.
+                //
+                // FIXME: clicking on the link should just open the file in the editor,
+                // instead of falling back to external urls.
+                Some(format!(
+                    "https://docs.rs/{krate}/{version}/",
+                    krate = name,
+                    version = version.as_deref().unwrap_or("*")
+                ))
+            });
+            (weblink, local_doc)
         }
     };
-    Url::parse(&base).ok()?.join(&format!("{display_name}/")).ok()
+    let web_base = web_base
+        .and_then(|it| Url::parse(&it).ok())
+        .and_then(|it| it.join(&format!("{display_name}/")).ok());
+    let local_base = local_base.and_then(|it| it.join(&format!("{display_name}/")).ok());
+
+    (web_base, local_base)
 }
 
 /// Get the filename and extension generated for a symbol by rustdoc.
@@ -490,9 +567,9 @@ fn filename_and_frag_for_def(
 
     let res = match def {
         Definition::Adt(adt) => match adt {
-            Adt::Struct(s) => format!("struct.{}.html", s.name(db)),
-            Adt::Enum(e) => format!("enum.{}.html", e.name(db)),
-            Adt::Union(u) => format!("union.{}.html", u.name(db)),
+            Adt::Struct(s) => format!("struct.{}.html", s.name(db).display(db.upcast())),
+            Adt::Enum(e) => format!("enum.{}.html", e.name(db).display(db.upcast())),
+            Adt::Union(u) => format!("union.{}.html", u.name(db).display(db.upcast())),
         },
         Definition::Module(m) => match m.name(db) {
             // `#[doc(keyword = "...")]` is internal used only by rust compiler
@@ -500,21 +577,25 @@ fn filename_and_frag_for_def(
                 Some(kw) => {
                     format!("keyword.{}.html", kw.trim_matches('"'))
                 }
-                None => format!("{name}/index.html"),
+                None => format!("{}/index.html", name.display(db.upcast())),
             },
             None => String::from("index.html"),
         },
-        Definition::Trait(t) => format!("trait.{}.html", t.name(db)),
-        Definition::TraitAlias(t) => format!("traitalias.{}.html", t.name(db)),
-        Definition::TypeAlias(t) => format!("type.{}.html", t.name(db)),
-        Definition::BuiltinType(t) => format!("primitive.{}.html", t.name()),
-        Definition::Function(f) => format!("fn.{}.html", f.name(db)),
+        Definition::Trait(t) => format!("trait.{}.html", t.name(db).display(db.upcast())),
+        Definition::TraitAlias(t) => format!("traitalias.{}.html", t.name(db).display(db.upcast())),
+        Definition::TypeAlias(t) => format!("type.{}.html", t.name(db).display(db.upcast())),
+        Definition::BuiltinType(t) => format!("primitive.{}.html", t.name().display(db.upcast())),
+        Definition::Function(f) => format!("fn.{}.html", f.name(db).display(db.upcast())),
         Definition::Variant(ev) => {
-            format!("enum.{}.html#variant.{}", ev.parent_enum(db).name(db), ev.name(db))
+            format!(
+                "enum.{}.html#variant.{}",
+                ev.parent_enum(db).name(db).display(db.upcast()),
+                ev.name(db).display(db.upcast())
+            )
         }
-        Definition::Const(c) => format!("const.{}.html", c.name(db)?),
-        Definition::Static(s) => format!("static.{}.html", s.name(db)),
-        Definition::Macro(mac) => format!("macro.{}.html", mac.name(db)),
+        Definition::Const(c) => format!("const.{}.html", c.name(db)?.display(db.upcast())),
+        Definition::Static(s) => format!("static.{}.html", s.name(db).display(db.upcast())),
+        Definition::Macro(mac) => format!("macro.{}.html", mac.name(db).display(db.upcast())),
         Definition::Field(field) => {
             let def = match field.parent_def(db) {
                 hir::VariantDef::Struct(it) => Definition::Adt(it.into()),
@@ -522,7 +603,11 @@ fn filename_and_frag_for_def(
                 hir::VariantDef::Variant(it) => Definition::Variant(it),
             };
             let (_, file, _) = filename_and_frag_for_def(db, def)?;
-            return Some((def, file, Some(format!("structfield.{}", field.name(db)))));
+            return Some((
+                def,
+                file,
+                Some(format!("structfield.{}", field.name(db).display(db.upcast()))),
+            ));
         }
         Definition::SelfType(impl_) => {
             let adt = impl_.self_ty(db).as_adt()?.into();
@@ -556,12 +641,14 @@ fn get_assoc_item_fragment(db: &dyn HirDatabase, assoc_item: hir::AssocItem) ->
             // Rustdoc makes this decision based on whether a method 'has defaultness'.
             // Currently this is only the case for provided trait methods.
             if is_trait_method && !function.has_body(db) {
-                format!("tymethod.{}", function.name(db))
+                format!("tymethod.{}", function.name(db).display(db.upcast()))
             } else {
-                format!("method.{}", function.name(db))
+                format!("method.{}", function.name(db).display(db.upcast()))
             }
         }
-        AssocItem::Const(constant) => format!("associatedconstant.{}", constant.name(db)?),
-        AssocItem::TypeAlias(ty) => format!("associatedtype.{}", ty.name(db)),
+        AssocItem::Const(constant) => {
+            format!("associatedconstant.{}", constant.name(db)?.display(db.upcast()))
+        }
+        AssocItem::TypeAlias(ty) => format!("associatedtype.{}", ty.name(db).display(db.upcast())),
     })
 }
diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs
index 104181a33e6..05a64b33bfd 100644
--- a/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs
@@ -1,3 +1,5 @@
+use std::ffi::OsStr;
+
 use expect_test::{expect, Expect};
 use hir::{HasAttrs, Semantics};
 use ide_db::{
@@ -13,11 +15,33 @@ use crate::{
     fixture, TryToNav,
 };
 
-fn check_external_docs(ra_fixture: &str, expect: Expect) {
+fn check_external_docs(
+    ra_fixture: &str,
+    target_dir: Option<&OsStr>,
+    expect_web_url: Option<Expect>,
+    expect_local_url: Option<Expect>,
+    sysroot: Option<&OsStr>,
+) {
     let (analysis, position) = fixture::position(ra_fixture);
-    let url = analysis.external_docs(position).unwrap().expect("could not find url for symbol");
+    let links = analysis.external_docs(position, target_dir, sysroot).unwrap();
+
+    let web_url = links.web_url;
+    let local_url = links.local_url;
+
+    println!("web_url: {:?}", web_url);
+    println!("local_url: {:?}", local_url);
+
+    match (expect_web_url, web_url) {
+        (Some(expect), Some(url)) => expect.assert_eq(&url),
+        (None, None) => (),
+        _ => panic!("Unexpected web url"),
+    }
 
-    expect.assert_eq(&url)
+    match (expect_local_url, local_url) {
+        (Some(expect), Some(url)) => expect.assert_eq(&url),
+        (None, None) => (),
+        _ => panic!("Unexpected local url"),
+    }
 }
 
 fn check_rewrite(ra_fixture: &str, expect: Expect) {
@@ -97,6 +121,20 @@ fn node_to_def(
 }
 
 #[test]
+fn external_docs_doc_builtin_type() {
+    check_external_docs(
+        r#"
+//- /main.rs crate:foo
+let x: u3$02 = 0;
+"#,
+        Some(&OsStr::new("/home/user/project")),
+        Some(expect![[r#"https://doc.rust-lang.org/nightly/core/primitive.u32.html"#]]),
+        Some(expect![[r#"file:///sysroot/share/doc/rust/html/core/primitive.u32.html"#]]),
+        Some(&OsStr::new("/sysroot")),
+    );
+}
+
+#[test]
 fn external_docs_doc_url_crate() {
     check_external_docs(
         r#"
@@ -105,7 +143,10 @@ use foo$0::Foo;
 //- /lib.rs crate:foo
 pub struct Foo;
 "#,
-        expect![[r#"https://docs.rs/foo/*/foo/index.html"#]],
+        Some(&OsStr::new("/home/user/project")),
+        Some(expect![[r#"https://docs.rs/foo/*/foo/index.html"#]]),
+        Some(expect![[r#"file:///home/user/project/doc/foo/index.html"#]]),
+        Some(&OsStr::new("/sysroot")),
     );
 }
 
@@ -116,7 +157,10 @@ fn external_docs_doc_url_std_crate() {
 //- /main.rs crate:std
 use self$0;
 "#,
-        expect![[r#"https://doc.rust-lang.org/nightly/std/index.html"#]],
+        Some(&OsStr::new("/home/user/project")),
+        Some(expect!["https://doc.rust-lang.org/stable/std/index.html"]),
+        Some(expect!["file:///sysroot/share/doc/rust/html/std/index.html"]),
+        Some(&OsStr::new("/sysroot")),
     );
 }
 
@@ -127,7 +171,38 @@ fn external_docs_doc_url_struct() {
 //- /main.rs crate:foo
 pub struct Fo$0o;
 "#,
-        expect![[r#"https://docs.rs/foo/*/foo/struct.Foo.html"#]],
+        Some(&OsStr::new("/home/user/project")),
+        Some(expect![[r#"https://docs.rs/foo/*/foo/struct.Foo.html"#]]),
+        Some(expect![[r#"file:///home/user/project/doc/foo/struct.Foo.html"#]]),
+        Some(&OsStr::new("/sysroot")),
+    );
+}
+
+#[test]
+fn external_docs_doc_url_windows_backslash_path() {
+    check_external_docs(
+        r#"
+//- /main.rs crate:foo
+pub struct Fo$0o;
+"#,
+        Some(&OsStr::new(r"C:\Users\user\project")),
+        Some(expect![[r#"https://docs.rs/foo/*/foo/struct.Foo.html"#]]),
+        Some(expect![[r#"file:///C:/Users/user/project/doc/foo/struct.Foo.html"#]]),
+        Some(&OsStr::new("/sysroot")),
+    );
+}
+
+#[test]
+fn external_docs_doc_url_windows_slash_path() {
+    check_external_docs(
+        r#"
+//- /main.rs crate:foo
+pub struct Fo$0o;
+"#,
+        Some(&OsStr::new(r"C:/Users/user/project")),
+        Some(expect![[r#"https://docs.rs/foo/*/foo/struct.Foo.html"#]]),
+        Some(expect![[r#"file:///C:/Users/user/project/doc/foo/struct.Foo.html"#]]),
+        Some(&OsStr::new("/sysroot")),
     );
 }
 
@@ -140,7 +215,10 @@ pub struct Foo {
     field$0: ()
 }
 "#,
-        expect![[r##"https://docs.rs/foo/*/foo/struct.Foo.html#structfield.field"##]],
+        None,
+        Some(expect![[r##"https://docs.rs/foo/*/foo/struct.Foo.html#structfield.field"##]]),
+        None,
+        None,
     );
 }
 
@@ -151,7 +229,10 @@ fn external_docs_doc_url_fn() {
 //- /main.rs crate:foo
 pub fn fo$0o() {}
 "#,
-        expect![[r#"https://docs.rs/foo/*/foo/fn.foo.html"#]],
+        None,
+        Some(expect![[r#"https://docs.rs/foo/*/foo/fn.foo.html"#]]),
+        None,
+        None,
     );
 }
 
@@ -165,7 +246,10 @@ impl Foo {
     pub fn method$0() {}
 }
 "#,
-        expect![[r##"https://docs.rs/foo/*/foo/struct.Foo.html#method.method"##]],
+        None,
+        Some(expect![[r##"https://docs.rs/foo/*/foo/struct.Foo.html#method.method"##]]),
+        None,
+        None,
     );
     check_external_docs(
         r#"
@@ -175,7 +259,10 @@ impl Foo {
     const CONST$0: () = ();
 }
 "#,
-        expect![[r##"https://docs.rs/foo/*/foo/struct.Foo.html#associatedconstant.CONST"##]],
+        None,
+        Some(expect![[r##"https://docs.rs/foo/*/foo/struct.Foo.html#associatedconstant.CONST"##]]),
+        None,
+        None,
     );
 }
 
@@ -192,7 +279,10 @@ impl Trait for Foo {
     pub fn method$0() {}
 }
 "#,
-        expect![[r##"https://docs.rs/foo/*/foo/struct.Foo.html#method.method"##]],
+        None,
+        Some(expect![[r##"https://docs.rs/foo/*/foo/struct.Foo.html#method.method"##]]),
+        None,
+        None,
     );
     check_external_docs(
         r#"
@@ -205,7 +295,10 @@ impl Trait for Foo {
     const CONST$0: () = ();
 }
 "#,
-        expect![[r##"https://docs.rs/foo/*/foo/struct.Foo.html#associatedconstant.CONST"##]],
+        None,
+        Some(expect![[r##"https://docs.rs/foo/*/foo/struct.Foo.html#associatedconstant.CONST"##]]),
+        None,
+        None,
     );
     check_external_docs(
         r#"
@@ -218,7 +311,10 @@ impl Trait for Foo {
     type Type$0 = ();
 }
 "#,
-        expect![[r##"https://docs.rs/foo/*/foo/struct.Foo.html#associatedtype.Type"##]],
+        None,
+        Some(expect![[r##"https://docs.rs/foo/*/foo/struct.Foo.html#associatedtype.Type"##]]),
+        None,
+        None,
     );
 }
 
@@ -231,7 +327,10 @@ pub trait Foo {
     fn method$0();
 }
 "#,
-        expect![[r##"https://docs.rs/foo/*/foo/trait.Foo.html#tymethod.method"##]],
+        None,
+        Some(expect![[r##"https://docs.rs/foo/*/foo/trait.Foo.html#tymethod.method"##]]),
+        None,
+        None,
     );
     check_external_docs(
         r#"
@@ -240,7 +339,10 @@ pub trait Foo {
     const CONST$0: ();
 }
 "#,
-        expect![[r##"https://docs.rs/foo/*/foo/trait.Foo.html#associatedconstant.CONST"##]],
+        None,
+        Some(expect![[r##"https://docs.rs/foo/*/foo/trait.Foo.html#associatedconstant.CONST"##]]),
+        None,
+        None,
     );
     check_external_docs(
         r#"
@@ -249,7 +351,10 @@ pub trait Foo {
     type Type$0;
 }
 "#,
-        expect![[r##"https://docs.rs/foo/*/foo/trait.Foo.html#associatedtype.Type"##]],
+        None,
+        Some(expect![[r##"https://docs.rs/foo/*/foo/trait.Foo.html#associatedtype.Type"##]]),
+        None,
+        None,
     );
 }
 
@@ -260,7 +365,10 @@ fn external_docs_trait() {
 //- /main.rs crate:foo
 trait Trait$0 {}
 "#,
-        expect![[r#"https://docs.rs/foo/*/foo/trait.Trait.html"#]],
+        None,
+        Some(expect![[r#"https://docs.rs/foo/*/foo/trait.Trait.html"#]]),
+        None,
+        None,
     )
 }
 
@@ -273,7 +381,10 @@ pub mod foo {
     pub mod ba$0r {}
 }
 "#,
-        expect![[r#"https://docs.rs/foo/*/foo/foo/bar/index.html"#]],
+        None,
+        Some(expect![[r#"https://docs.rs/foo/*/foo/foo/bar/index.html"#]]),
+        None,
+        None,
     )
 }
 
@@ -294,7 +405,10 @@ fn foo() {
     let bar: wrapper::It$0em;
 }
         "#,
-        expect![[r#"https://docs.rs/foo/*/foo/wrapper/module/struct.Item.html"#]],
+        None,
+        Some(expect![[r#"https://docs.rs/foo/*/foo/wrapper/module/struct.Item.html"#]]),
+        None,
+        None,
     )
 }
 
diff --git a/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs b/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs
index 418043d6798..d6bbf2bf794 100644
--- a/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs
@@ -76,15 +76,17 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<
         if let Some(item) = ast::Item::cast(node.clone()) {
             if let Some(def) = sema.resolve_attr_macro_call(&item) {
                 break (
-                    def.name(db).to_string(),
+                    def.name(db).display(db).to_string(),
                     expand_attr_macro_recur(&sema, &item)?,
                     SyntaxKind::MACRO_ITEMS,
                 );
             }
         }
         if let Some(mac) = ast::MacroCall::cast(node) {
+            let mut name = mac.path()?.segment()?.name_ref()?.to_string();
+            name.push('!');
             break (
-                mac.path()?.segment()?.name_ref()?.to_string(),
+                name,
                 expand_macro_recur(&sema, &mac)?,
                 mac.syntax().parent().map(|it| it.kind()).unwrap_or(SyntaxKind::MACRO_ITEMS),
             );
@@ -149,9 +151,11 @@ fn _format(
     _db: &RootDatabase,
     _kind: SyntaxKind,
     _file_id: FileId,
-    _expansion: &str,
+    expansion: &str,
 ) -> Option<String> {
-    None
+    // remove trailing spaces for test
+    use itertools::Itertools;
+    Some(expansion.lines().map(|x| x.trim_end()).join("\n"))
 }
 
 #[cfg(not(any(test, target_arch = "wasm32", target_os = "emscripten")))]
@@ -235,7 +239,7 @@ fn main() {
 }
 "#,
             expect![[r#"
-                bar
+                bar!
                 5i64 as _"#]],
         );
     }
@@ -252,7 +256,7 @@ fn main() {
 }
 "#,
             expect![[r#"
-                bar
+                bar!
                 for _ in 0..42{}"#]],
         );
     }
@@ -273,9 +277,8 @@ macro_rules! baz {
 f$0oo!();
 "#,
             expect![[r#"
-                foo
-                fn b(){}
-            "#]],
+                foo!
+                fn b(){}"#]],
         );
     }
 
@@ -294,7 +297,7 @@ macro_rules! foo {
 f$0oo!();
         "#,
             expect![[r#"
-                foo
+                foo!
                 fn some_thing() -> u32 {
                   let a = 0;
                   a+10
@@ -328,16 +331,16 @@ fn main() {
 }
 "#,
             expect![[r#"
-       match_ast
-       {
-         if let Some(it) = ast::TraitDef::cast(container.clone()){}
-         else if let Some(it) = ast::ImplDef::cast(container.clone()){}
-         else {
-           {
-             continue
-           }
-         }
-       }"#]],
+                match_ast!
+                {
+                  if let Some(it) = ast::TraitDef::cast(container.clone()){}
+                  else if let Some(it) = ast::ImplDef::cast(container.clone()){}
+                  else {
+                    {
+                      continue
+                    }
+                  }
+                }"#]],
         );
     }
 
@@ -358,7 +361,7 @@ fn main() {
 }
 "#,
             expect![[r#"
-                match_ast
+                match_ast!
                 {}"#]],
         );
     }
@@ -383,7 +386,7 @@ fn main() {
 }
             "#,
             expect![[r#"
-                foo
+                foo!
                 {
                   macro_rules! bar {
                     () => {
@@ -411,7 +414,7 @@ fn main() {
 }
 "#,
             expect![[r#"
-                foo
+                foo!
             "#]],
         );
     }
@@ -433,7 +436,7 @@ fn main() {
 }
 "#,
             expect![[r#"
-                foo
+                foo!
                 0"#]],
         );
     }
@@ -451,7 +454,7 @@ fn main() {
 }
 "#,
             expect![[r#"
-                foo
+                foo!
                 fn f<T>(_: &dyn ::std::marker::Copy){}"#]],
         );
     }
@@ -469,8 +472,17 @@ struct Foo {}
 "#,
             expect![[r#"
                 Clone
-                impl < >core::clone::Clone for Foo< >{}
-            "#]],
+                impl < >core::clone::Clone for Foo< >where {
+                  fn clone(&self) -> Self {
+                    match self {
+                      Foo{}
+                       => Foo{}
+                      ,
+
+                      }
+                  }
+
+                  }"#]],
         );
     }
 
@@ -486,8 +498,7 @@ struct Foo {}
 "#,
             expect![[r#"
                 Copy
-                impl < >core::marker::Copy for Foo< >{}
-            "#]],
+                impl < >core::marker::Copy for Foo< >where{}"#]],
         );
     }
 
@@ -502,8 +513,7 @@ struct Foo {}
 "#,
             expect![[r#"
                 Copy
-                impl < >core::marker::Copy for Foo< >{}
-            "#]],
+                impl < >core::marker::Copy for Foo< >where{}"#]],
         );
         check(
             r#"
@@ -514,8 +524,17 @@ struct Foo {}
 "#,
             expect![[r#"
                 Clone
-                impl < >core::clone::Clone for Foo< >{}
-            "#]],
+                impl < >core::clone::Clone for Foo< >where {
+                  fn clone(&self) -> Self {
+                    match self {
+                      Foo{}
+                       => Foo{}
+                      ,
+
+                      }
+                  }
+
+                  }"#]],
         );
     }
 }
diff --git a/src/tools/rust-analyzer/crates/ide/src/extend_selection.rs b/src/tools/rust-analyzer/crates/ide/src/extend_selection.rs
index 9f78c75e90a..f9061822244 100644
--- a/src/tools/rust-analyzer/crates/ide/src/extend_selection.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/extend_selection.rs
@@ -39,7 +39,7 @@ fn try_extend_selection(
 ) -> Option<TextRange> {
     let range = frange.range;
 
-    let string_kinds = [COMMENT, STRING, BYTE_STRING];
+    let string_kinds = [COMMENT, STRING, BYTE_STRING, C_STRING];
     let list_kinds = [
         RECORD_PAT_FIELD_LIST,
         MATCH_ARM_LIST,
diff --git a/src/tools/rust-analyzer/crates/ide/src/fetch_crates.rs b/src/tools/rust-analyzer/crates/ide/src/fetch_crates.rs
new file mode 100644
index 00000000000..46ee671defb
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide/src/fetch_crates.rs
@@ -0,0 +1,42 @@
+use ide_db::{
+    base_db::{CrateOrigin, FileId, SourceDatabase},
+    FxIndexSet, RootDatabase,
+};
+
+#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub struct CrateInfo {
+    pub name: Option<String>,
+    pub version: Option<String>,
+    pub root_file_id: FileId,
+}
+
+// Feature: Show Dependency Tree
+//
+// Shows a view tree with all the dependencies of this project
+//
+// |===
+// | Editor  | Panel Name
+//
+// | VS Code | **Rust Dependencies**
+// |===
+//
+// image::https://user-images.githubusercontent.com/5748995/229394139-2625beab-f4c9-484b-84ed-ad5dee0b1e1a.png[]
+pub(crate) fn fetch_crates(db: &RootDatabase) -> FxIndexSet<CrateInfo> {
+    let crate_graph = db.crate_graph();
+    crate_graph
+        .iter()
+        .map(|crate_id| &crate_graph[crate_id])
+        .filter(|&data| !matches!(data.origin, CrateOrigin::Local { .. }))
+        .map(|data| crate_info(data))
+        .collect()
+}
+
+fn crate_info(data: &ide_db::base_db::CrateData) -> CrateInfo {
+    let crate_name = crate_name(data);
+    let version = data.version.clone();
+    CrateInfo { name: crate_name, version, root_file_id: data.root_file_id }
+}
+
+fn crate_name(data: &ide_db::base_db::CrateData) -> Option<String> {
+    data.display_name.as_ref().map(|it| it.canonical_name().to_owned())
+}
diff --git a/src/tools/rust-analyzer/crates/ide/src/file_structure.rs b/src/tools/rust-analyzer/crates/ide/src/file_structure.rs
index a32ac35496a..b278924721c 100644
--- a/src/tools/rust-analyzer/crates/ide/src/file_structure.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/file_structure.rs
@@ -219,7 +219,7 @@ mod tests {
     }
 
     #[test]
-    fn test_nagative_trait_bound() {
+    fn test_negative_trait_bound() {
         let txt = r#"impl !Unpin for Test {}"#;
         check(
             txt,
diff --git a/src/tools/rust-analyzer/crates/ide/src/fixture.rs b/src/tools/rust-analyzer/crates/ide/src/fixture.rs
index 2ea6f6a9ab1..2e5903c0602 100644
--- a/src/tools/rust-analyzer/crates/ide/src/fixture.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/fixture.rs
@@ -1,5 +1,4 @@
 //! Utilities for creating `Analysis` instances for tests.
-use hir::db::DefDatabase;
 use ide_db::base_db::fixture::ChangeFixture;
 use test_utils::{extract_annotations, RangeOrOffset};
 
@@ -9,7 +8,7 @@ use crate::{Analysis, AnalysisHost, FileId, FilePosition, FileRange};
 pub(crate) fn file(ra_fixture: &str) -> (Analysis, FileId) {
     let mut host = AnalysisHost::default();
     let change_fixture = ChangeFixture::parse(ra_fixture);
-    host.db.set_enable_proc_attr_macros(true);
+    host.db.enable_proc_attr_macros();
     host.db.apply_change(change_fixture.change);
     (host.analysis(), change_fixture.files[0])
 }
@@ -18,7 +17,7 @@ pub(crate) fn file(ra_fixture: &str) -> (Analysis, FileId) {
 pub(crate) fn position(ra_fixture: &str) -> (Analysis, FilePosition) {
     let mut host = AnalysisHost::default();
     let change_fixture = ChangeFixture::parse(ra_fixture);
-    host.db.set_enable_proc_attr_macros(true);
+    host.db.enable_proc_attr_macros();
     host.db.apply_change(change_fixture.change);
     let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)");
     let offset = range_or_offset.expect_offset();
@@ -29,7 +28,7 @@ pub(crate) fn position(ra_fixture: &str) -> (Analysis, FilePosition) {
 pub(crate) fn range(ra_fixture: &str) -> (Analysis, FileRange) {
     let mut host = AnalysisHost::default();
     let change_fixture = ChangeFixture::parse(ra_fixture);
-    host.db.set_enable_proc_attr_macros(true);
+    host.db.enable_proc_attr_macros();
     host.db.apply_change(change_fixture.change);
     let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)");
     let range = range_or_offset.expect_range();
@@ -40,7 +39,7 @@ pub(crate) fn range(ra_fixture: &str) -> (Analysis, FileRange) {
 pub(crate) fn range_or_position(ra_fixture: &str) -> (Analysis, FileId, RangeOrOffset) {
     let mut host = AnalysisHost::default();
     let change_fixture = ChangeFixture::parse(ra_fixture);
-    host.db.set_enable_proc_attr_macros(true);
+    host.db.enable_proc_attr_macros();
     host.db.apply_change(change_fixture.change);
     let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)");
     (host.analysis(), file_id, range_or_offset)
@@ -50,7 +49,7 @@ pub(crate) fn range_or_position(ra_fixture: &str) -> (Analysis, FileId, RangeOrO
 pub(crate) fn annotations(ra_fixture: &str) -> (Analysis, FilePosition, Vec<(FileRange, String)>) {
     let mut host = AnalysisHost::default();
     let change_fixture = ChangeFixture::parse(ra_fixture);
-    host.db.set_enable_proc_attr_macros(true);
+    host.db.enable_proc_attr_macros();
     host.db.apply_change(change_fixture.change);
     let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)");
     let offset = range_or_offset.expect_offset();
@@ -67,11 +66,11 @@ pub(crate) fn annotations(ra_fixture: &str) -> (Analysis, FilePosition, Vec<(Fil
     (host.analysis(), FilePosition { file_id, offset }, annotations)
 }
 
-/// Creates analysis from a multi-file fixture with annonations without $0
+/// Creates analysis from a multi-file fixture with annotations without $0
 pub(crate) fn annotations_without_marker(ra_fixture: &str) -> (Analysis, Vec<(FileRange, String)>) {
     let mut host = AnalysisHost::default();
     let change_fixture = ChangeFixture::parse(ra_fixture);
-    host.db.set_enable_proc_attr_macros(true);
+    host.db.enable_proc_attr_macros();
     host.db.apply_change(change_fixture.change);
 
     let annotations = change_fixture
diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs
index cf0819a2524..4e641357e37 100644
--- a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs
@@ -113,6 +113,7 @@ fn try_lookup_include_path(
         file_id,
         full_range: TextRange::new(0.into(), size),
         name: path.into(),
+        alias: None,
         focus_range: None,
         kind: None,
         container_name: None,
@@ -833,8 +834,7 @@ fn test() {
 #[rustc_builtin_macro]
 macro_rules! include {}
 
-  include!("foo.rs");
-//^^^^^^^^^^^^^^^^^^^
+include!("foo.rs");
 
 fn f() {
     foo$0();
@@ -846,6 +846,33 @@ mod confuse_index {
 
 //- /foo.rs
 fn foo() {}
+ //^^^
+        "#,
+        );
+    }
+
+    #[test]
+    fn goto_through_included_file_struct_with_doc_comment() {
+        check(
+            r#"
+//- /main.rs
+#[rustc_builtin_macro]
+macro_rules! include {}
+
+include!("foo.rs");
+
+fn f() {
+    let x = Foo$0;
+}
+
+mod confuse_index {
+    pub struct Foo;
+}
+
+//- /foo.rs
+/// This is a doc comment
+pub struct Foo;
+         //^^^
         "#,
         );
     }
@@ -1471,6 +1498,29 @@ fn f() {
             );
         }
         #[test]
+        fn method_call_inside_block() {
+            check(
+                r#"
+trait Twait {
+    fn a(&self);
+}
+
+fn outer() {
+    struct Stwuct;
+
+    impl Twait for Stwuct {
+        fn a(&self){}
+         //^
+    }
+    fn f() {
+        let s = Stwuct;
+        s.a$0();
+    }
+}
+        "#,
+            );
+        }
+        #[test]
         fn path_call() {
             check(
                 r#"
diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs
index 6d2d0bd6351..6048990f749 100644
--- a/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs
@@ -10,7 +10,7 @@ use crate::{FilePosition, NavigationTarget, RangeInfo, TryToNav};
 // |===
 // | Editor  | Action Name
 //
-// | VS Code | **Go to Type Definition*
+// | VS Code | **Go to Type Definition**
 // |===
 //
 // image::https://user-images.githubusercontent.com/48062697/113020657-b560f500-917a-11eb-9007-0f809733a338.gif[]
@@ -38,32 +38,41 @@ pub(crate) fn goto_type_definition(
     };
     let range = token.text_range();
     sema.descend_into_macros(token)
-        .iter()
+        .into_iter()
         .filter_map(|token| {
-            let ty = sema.token_ancestors_with_macros(token.clone()).find_map(|node| {
-                let ty = match_ast! {
-                    match node {
-                        ast::Expr(it) => sema.type_of_expr(&it)?.original,
-                        ast::Pat(it) => sema.type_of_pat(&it)?.original,
-                        ast::SelfParam(it) => sema.type_of_self(&it)?,
-                        ast::Type(it) => sema.resolve_type(&it)?,
-                        ast::RecordField(it) => sema.to_def(&it).map(|d| d.ty(db.upcast()))?,
-                        // can't match on RecordExprField directly as `ast::Expr` will match an iteration too early otherwise
-                        ast::NameRef(it) => {
-                            if let Some(record_field) = ast::RecordExprField::for_name_ref(&it) {
-                                let (_, _, ty) = sema.resolve_record_field(&record_field)?;
-                                ty
-                            } else {
-                                let record_field = ast::RecordPatField::for_field_name_ref(&it)?;
-                                sema.resolve_record_pat_field(&record_field)?.1
-                            }
-                        },
-                        _ => return None,
-                    }
-                };
+            let ty = sema
+                .token_ancestors_with_macros(token)
+                // When `token` is within a macro call, we can't determine its type. Don't continue
+                // this traversal because otherwise we'll end up returning the type of *that* macro
+                // call, which is not what we want in general.
+                //
+                // Macro calls always wrap `TokenTree`s, so it's sufficient and efficient to test
+                // if the current node is a `TokenTree`.
+                .take_while(|node| !ast::TokenTree::can_cast(node.kind()))
+                .find_map(|node| {
+                    let ty = match_ast! {
+                        match node {
+                            ast::Expr(it) => sema.type_of_expr(&it)?.original,
+                            ast::Pat(it) => sema.type_of_pat(&it)?.original,
+                            ast::SelfParam(it) => sema.type_of_self(&it)?,
+                            ast::Type(it) => sema.resolve_type(&it)?,
+                            ast::RecordField(it) => sema.to_def(&it)?.ty(db.upcast()),
+                            // can't match on RecordExprField directly as `ast::Expr` will match an iteration too early otherwise
+                            ast::NameRef(it) => {
+                                if let Some(record_field) = ast::RecordExprField::for_name_ref(&it) {
+                                    let (_, _, ty) = sema.resolve_record_field(&record_field)?;
+                                    ty
+                                } else {
+                                    let record_field = ast::RecordPatField::for_field_name_ref(&it)?;
+                                    sema.resolve_record_pat_field(&record_field)?.1
+                                }
+                            },
+                            _ => return None,
+                        }
+                    };
 
-                Some(ty)
-            });
+                    Some(ty)
+                });
             ty
         })
         .for_each(|ty| {
@@ -94,7 +103,7 @@ mod tests {
     fn check(ra_fixture: &str) {
         let (analysis, position, expected) = fixture::annotations(ra_fixture);
         let navs = analysis.goto_type_definition(position).unwrap().unwrap().info;
-        assert_ne!(navs.len(), 0);
+        assert!(!navs.is_empty(), "navigation is empty");
 
         let cmp = |&FileRange { file_id, range }: &_| (file_id, range.start());
         let navs = navs
@@ -104,7 +113,7 @@ mod tests {
             .collect::<Vec<_>>();
         let expected = expected
             .into_iter()
-            .map(|(FileRange { file_id, range }, _)| FileRange { file_id, range })
+            .map(|(file_range, _)| file_range)
             .sorted_by_key(cmp)
             .collect::<Vec<_>>();
         assert_eq!(expected, navs);
@@ -199,6 +208,32 @@ id! {
     }
 
     #[test]
+    fn dont_collect_type_from_token_in_macro_call() {
+        check(
+            r#"
+struct DontCollectMe;
+struct S;
+     //^
+
+macro_rules! inner {
+    ($t:tt) => { DontCollectMe }
+}
+macro_rules! m {
+    ($t:ident) => {
+        match $t {
+            _ => inner!($t);
+        }
+    }
+}
+
+fn test() {
+    m!($0S);
+}
+"#,
+        );
+    }
+
+    #[test]
     fn goto_type_definition_for_param() {
         check(
             r#"
diff --git a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs
index d88ffd25c40..7e545491f8e 100644
--- a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs
@@ -1,10 +1,12 @@
 use hir::Semantics;
 use ide_db::{
-    base_db::{FileId, FilePosition},
+    base_db::{FileId, FilePosition, FileRange},
     defs::{Definition, IdentClass},
     helpers::pick_best_token,
     search::{FileReference, ReferenceCategory, SearchScope},
-    syntax_helpers::node_ext::{for_each_break_and_continue_expr, for_each_tail_expr, walk_expr},
+    syntax_helpers::node_ext::{
+        for_each_break_and_continue_expr, for_each_tail_expr, full_path_of_name_ref, walk_expr,
+    },
     FxHashSet, RootDatabase,
 };
 use syntax::{
@@ -30,6 +32,7 @@ pub struct HighlightRelatedConfig {
     pub references: bool,
     pub exit_points: bool,
     pub break_points: bool,
+    pub closure_captures: bool,
     pub yield_points: bool,
 }
 
@@ -38,11 +41,13 @@ pub struct HighlightRelatedConfig {
 // Highlights constructs related to the thing under the cursor:
 //
 // . if on an identifier, highlights all references to that identifier in the current file
+// .. additionally, if the identifier is a trait in a where clause, type parameter trait bound or use item, highlights all references to that trait's assoc items in the corresponding scope
 // . if on an `async` or `await token, highlights all yield points for that async context
 // . if on a `return` or `fn` keyword, `?` character or `->` return type arrow, highlights all exit points for that context
 // . if on a `break`, `loop`, `while` or `for` token, highlights all break points for that loop or block context
+// . if on a `move` or `|` token that belongs to a closure, highlights all captures of the closure.
 //
-// Note: `?` and `->` do not currently trigger this behavior in the VSCode editor.
+// Note: `?`, `|` and `->` do not currently trigger this behavior in the VSCode editor.
 pub(crate) fn highlight_related(
     sema: &Semantics<'_, RootDatabase>,
     config: HighlightRelatedConfig,
@@ -53,11 +58,13 @@ pub(crate) fn highlight_related(
 
     let token = pick_best_token(syntax.token_at_offset(offset), |kind| match kind {
         T![?] => 4, // prefer `?` when the cursor is sandwiched like in `await$0?`
-        T![->] => 3,
-        kind if kind.is_keyword() => 2,
-        IDENT | INT_NUMBER => 1,
+        T![->] => 4,
+        kind if kind.is_keyword() => 3,
+        IDENT | INT_NUMBER => 2,
+        T![|] => 1,
         _ => 0,
     })?;
+    // most if not all of these should be re-implemented with information seeded from hir
     match token.kind() {
         T![?] if config.exit_points && token.parent().and_then(ast::TryExpr::cast).is_some() => {
             highlight_exit_points(sema, token)
@@ -70,18 +77,64 @@ pub(crate) fn highlight_related(
         T![break] | T![loop] | T![while] | T![continue] if config.break_points => {
             highlight_break_points(token)
         }
+        T![|] if config.closure_captures => highlight_closure_captures(sema, token, file_id),
+        T![move] if config.closure_captures => highlight_closure_captures(sema, token, file_id),
         _ if config.references => highlight_references(sema, &syntax, token, file_id),
         _ => None,
     }
 }
 
+fn highlight_closure_captures(
+    sema: &Semantics<'_, RootDatabase>,
+    token: SyntaxToken,
+    file_id: FileId,
+) -> Option<Vec<HighlightedRange>> {
+    let closure = token.parent_ancestors().take(2).find_map(ast::ClosureExpr::cast)?;
+    let search_range = closure.body()?.syntax().text_range();
+    let ty = &sema.type_of_expr(&closure.into())?.original;
+    let c = ty.as_closure()?;
+    Some(
+        c.captured_items(sema.db)
+            .into_iter()
+            .map(|capture| capture.local())
+            .flat_map(|local| {
+                let usages = Definition::Local(local)
+                    .usages(sema)
+                    .set_scope(Some(SearchScope::file_range(FileRange {
+                        file_id,
+                        range: search_range,
+                    })))
+                    .include_self_refs()
+                    .all()
+                    .references
+                    .remove(&file_id)
+                    .into_iter()
+                    .flatten()
+                    .map(|FileReference { category, range, .. }| HighlightedRange {
+                        range,
+                        category,
+                    });
+                let category = local.is_mut(sema.db).then_some(ReferenceCategory::Write);
+                local
+                    .sources(sema.db)
+                    .into_iter()
+                    .map(|x| x.to_nav(sema.db))
+                    .filter(|decl| decl.file_id == file_id)
+                    .filter_map(|decl| decl.focus_range)
+                    .map(move |range| HighlightedRange { range, category })
+                    .chain(usages)
+            })
+            .collect(),
+    )
+}
+
 fn highlight_references(
     sema: &Semantics<'_, RootDatabase>,
     node: &SyntaxNode,
     token: SyntaxToken,
     file_id: FileId,
 ) -> Option<Vec<HighlightedRange>> {
-    let defs = find_defs(sema, token);
+    let defs = find_defs(sema, token.clone());
     let usages = defs
         .iter()
         .filter_map(|&d| {
@@ -93,12 +146,62 @@ fn highlight_references(
                 .remove(&file_id)
         })
         .flatten()
-        .map(|FileReference { category: access, range, .. }| HighlightedRange {
-            range,
-            category: access,
-        });
+        .map(|FileReference { category, range, .. }| HighlightedRange { range, category });
     let mut res = FxHashSet::default();
     for &def in &defs {
+        // highlight trait usages
+        if let Definition::Trait(t) = def {
+            let trait_item_use_scope = (|| {
+                let name_ref = token.parent().and_then(ast::NameRef::cast)?;
+                let path = full_path_of_name_ref(&name_ref)?;
+                let parent = path.syntax().parent()?;
+                match_ast! {
+                    match parent {
+                        ast::UseTree(it) => it.syntax().ancestors().find(|it| {
+                            ast::SourceFile::can_cast(it.kind()) || ast::Module::can_cast(it.kind())
+                        }),
+                        ast::PathType(it) => it
+                            .syntax()
+                            .ancestors()
+                            .nth(2)
+                            .and_then(ast::TypeBoundList::cast)?
+                            .syntax()
+                            .parent()
+                            .filter(|it| ast::WhereClause::can_cast(it.kind()) || ast::TypeParam::can_cast(it.kind()))?
+                            .ancestors()
+                            .find(|it| {
+                                ast::Item::can_cast(it.kind())
+                            }),
+                        _ => None,
+                    }
+                }
+            })();
+            if let Some(trait_item_use_scope) = trait_item_use_scope {
+                res.extend(
+                    t.items_with_supertraits(sema.db)
+                        .into_iter()
+                        .filter_map(|item| {
+                            Definition::from(item)
+                                .usages(sema)
+                                .set_scope(Some(SearchScope::file_range(FileRange {
+                                    file_id,
+                                    range: trait_item_use_scope.text_range(),
+                                })))
+                                .include_self_refs()
+                                .all()
+                                .references
+                                .remove(&file_id)
+                        })
+                        .flatten()
+                        .map(|FileReference { category, range, .. }| HighlightedRange {
+                            range,
+                            category,
+                        }),
+                );
+            }
+        }
+
+        // highlight the defs themselves
         match def {
             Definition::Local(local) => {
                 let category = local.is_mut(sema.db).then_some(ReferenceCategory::Write);
@@ -148,9 +251,16 @@ fn highlight_exit_points(
 ) -> Option<Vec<HighlightedRange>> {
     fn hl(
         sema: &Semantics<'_, RootDatabase>,
+        def_ranges: [Option<TextRange>; 2],
         body: Option<ast::Expr>,
     ) -> Option<Vec<HighlightedRange>> {
         let mut highlights = Vec::new();
+        highlights.extend(
+            def_ranges
+                .into_iter()
+                .flatten()
+                .map(|range| HighlightedRange { category: None, range }),
+        );
         let body = body?;
         walk_expr(&body, &mut |expr| match expr {
             ast::Expr::ReturnExpr(expr) => {
@@ -194,10 +304,21 @@ fn highlight_exit_points(
     for anc in token.parent_ancestors() {
         return match_ast! {
             match anc {
-                ast::Fn(fn_) => hl(sema, fn_.body().map(ast::Expr::BlockExpr)),
-                ast::ClosureExpr(closure) => hl(sema, closure.body()),
+                ast::Fn(fn_) => hl(sema, [fn_.fn_token().map(|it| it.text_range()), None], fn_.body().map(ast::Expr::BlockExpr)),
+                ast::ClosureExpr(closure) => hl(
+                    sema,
+                    closure.param_list().map_or([None; 2], |p| [p.l_paren_token().map(|it| it.text_range()), p.r_paren_token().map(|it| it.text_range())]),
+                    closure.body()
+                ),
                 ast::BlockExpr(block_expr) => if matches!(block_expr.modifier(), Some(ast::BlockModifier::Async(_) | ast::BlockModifier::Try(_)| ast::BlockModifier::Const(_))) {
-                    hl(sema, Some(block_expr.into()))
+                    hl(
+                        sema,
+                        [block_expr.modifier().and_then(|modifier| match modifier {
+                            ast::BlockModifier::Async(t) | ast::BlockModifier::Try(t) | ast::BlockModifier::Const(t) => Some(t.text_range()),
+                            _ => None,
+                        }), None],
+                        Some(block_expr.into())
+                    )
                 } else {
                     continue;
                 },
@@ -352,16 +473,17 @@ mod tests {
 
     use super::*;
 
+    const ENABLED_CONFIG: HighlightRelatedConfig = HighlightRelatedConfig {
+        break_points: true,
+        exit_points: true,
+        references: true,
+        closure_captures: true,
+        yield_points: true,
+    };
+
     #[track_caller]
     fn check(ra_fixture: &str) {
-        let config = HighlightRelatedConfig {
-            break_points: true,
-            exit_points: true,
-            references: true,
-            yield_points: true,
-        };
-
-        check_with_config(ra_fixture, config);
+        check_with_config(ra_fixture, ENABLED_CONFIG);
     }
 
     #[track_caller]
@@ -571,6 +693,29 @@ pub async$0 fn foo() {
     }
 
     #[test]
+    fn test_hl_let_else_yield_points() {
+        check(
+            r#"
+pub async fn foo() {
+ // ^^^^^
+    let x = foo()
+        .await$0
+      // ^^^^^
+        .await;
+      // ^^^^^
+    || { 0.await };
+    let Some(_) = None else {
+        foo().await
+           // ^^^^^
+    };
+    (async { 0.await }).await
+                     // ^^^^^
+}
+"#,
+        );
+    }
+
+    #[test]
     fn test_hl_yield_nested_fn() {
         check(
             r#"
@@ -610,7 +755,8 @@ async fn foo() {
     fn test_hl_exit_points() {
         check(
             r#"
-fn foo() -> u32 {
+  fn foo() -> u32 {
+//^^
     if true {
         return$0 0;
      // ^^^^^^
@@ -629,7 +775,8 @@ fn foo() -> u32 {
     fn test_hl_exit_points2() {
         check(
             r#"
-fn foo() ->$0 u32 {
+  fn foo() ->$0 u32 {
+//^^
     if true {
         return 0;
      // ^^^^^^
@@ -648,7 +795,8 @@ fn foo() ->$0 u32 {
     fn test_hl_exit_points3() {
         check(
             r#"
-fn$0 foo() -> u32 {
+  fn$0 foo() -> u32 {
+//^^
     if true {
         return 0;
      // ^^^^^^
@@ -664,6 +812,26 @@ fn$0 foo() -> u32 {
     }
 
     #[test]
+    fn test_hl_let_else_exit_points() {
+        check(
+            r#"
+  fn$0 foo() -> u32 {
+//^^
+    let Some(bar) = None else {
+        return 0;
+     // ^^^^^^
+    };
+
+    0?;
+  // ^
+    0xDEAD_BEEF
+ // ^^^^^^^^^^^
+}
+"#,
+        );
+    }
+
+    #[test]
     fn test_hl_prefer_ref_over_tail_exit() {
         check(
             r#"
@@ -694,7 +862,8 @@ macro_rules! never {
     () => { never() }
 }
 fn never() -> ! { loop {} }
-fn foo() ->$0 u32 {
+  fn foo() ->$0 u32 {
+//^^
     never();
  // ^^^^^^^
     never!();
@@ -714,7 +883,8 @@ fn foo() ->$0 u32 {
     fn test_hl_inner_tail_exit_points() {
         check(
             r#"
-fn foo() ->$0 u32 {
+  fn foo() ->$0 u32 {
+//^^
     if true {
         unsafe {
             return 5;
@@ -755,7 +925,8 @@ fn foo() ->$0 u32 {
     fn test_hl_inner_tail_exit_points_labeled_block() {
         check(
             r#"
-fn foo() ->$0 u32 {
+  fn foo() ->$0 u32 {
+//^^
     'foo: {
         break 'foo 0;
      // ^^^^^
@@ -776,7 +947,8 @@ fn foo() ->$0 u32 {
     fn test_hl_inner_tail_exit_points_loops() {
         check(
             r#"
-fn foo() ->$0 u32 {
+  fn foo() ->$0 u32 {
+//^^
     'foo: while { return 0; true } {
                // ^^^^^^
         break 'foo 0;
@@ -1086,12 +1258,7 @@ fn function(field: u32) {
 
     #[test]
     fn test_hl_disabled_ref_local() {
-        let config = HighlightRelatedConfig {
-            references: false,
-            break_points: true,
-            exit_points: true,
-            yield_points: true,
-        };
+        let config = HighlightRelatedConfig { references: false, ..ENABLED_CONFIG };
 
         check_with_config(
             r#"
@@ -1106,12 +1273,7 @@ fn foo() {
 
     #[test]
     fn test_hl_disabled_ref_local_preserved_break() {
-        let config = HighlightRelatedConfig {
-            references: false,
-            break_points: true,
-            exit_points: true,
-            yield_points: true,
-        };
+        let config = HighlightRelatedConfig { references: false, ..ENABLED_CONFIG };
 
         check_with_config(
             r#"
@@ -1146,12 +1308,7 @@ fn foo() {
 
     #[test]
     fn test_hl_disabled_ref_local_preserved_yield() {
-        let config = HighlightRelatedConfig {
-            references: false,
-            break_points: true,
-            exit_points: true,
-            yield_points: true,
-        };
+        let config = HighlightRelatedConfig { references: false, ..ENABLED_CONFIG };
 
         check_with_config(
             r#"
@@ -1182,12 +1339,7 @@ async fn foo() {
 
     #[test]
     fn test_hl_disabled_ref_local_preserved_exit() {
-        let config = HighlightRelatedConfig {
-            references: false,
-            break_points: true,
-            exit_points: true,
-            yield_points: true,
-        };
+        let config = HighlightRelatedConfig { references: false, ..ENABLED_CONFIG };
 
         check_with_config(
             r#"
@@ -1207,7 +1359,8 @@ fn foo() -> i32 {
 
         check_with_config(
             r#"
-fn foo() ->$0 i32 {
+  fn foo() ->$0 i32 {
+//^^
     let x = 5;
     let y = x * 2;
 
@@ -1225,12 +1378,7 @@ fn foo() ->$0 i32 {
 
     #[test]
     fn test_hl_disabled_break() {
-        let config = HighlightRelatedConfig {
-            references: true,
-            break_points: false,
-            exit_points: true,
-            yield_points: true,
-        };
+        let config = HighlightRelatedConfig { break_points: false, ..ENABLED_CONFIG };
 
         check_with_config(
             r#"
@@ -1246,12 +1394,7 @@ fn foo() {
 
     #[test]
     fn test_hl_disabled_yield() {
-        let config = HighlightRelatedConfig {
-            references: true,
-            break_points: true,
-            exit_points: true,
-            yield_points: false,
-        };
+        let config = HighlightRelatedConfig { yield_points: false, ..ENABLED_CONFIG };
 
         check_with_config(
             r#"
@@ -1265,12 +1408,7 @@ async$0 fn foo() {
 
     #[test]
     fn test_hl_disabled_exit() {
-        let config = HighlightRelatedConfig {
-            references: true,
-            break_points: true,
-            exit_points: false,
-            yield_points: true,
-        };
+        let config = HighlightRelatedConfig { exit_points: false, ..ENABLED_CONFIG };
 
         check_with_config(
             r#"
@@ -1414,4 +1552,73 @@ impl Trait for () {
 "#,
         );
     }
+
+    #[test]
+    fn test_closure_capture_pipe() {
+        check(
+            r#"
+fn f() {
+    let x = 1;
+    //  ^
+    let c = $0|y| x + y;
+    //          ^ read
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn test_closure_capture_move() {
+        check(
+            r#"
+fn f() {
+    let x = 1;
+    //  ^
+    let c = move$0 |y| x + y;
+    //               ^ read
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn test_trait_highlights_assoc_item_uses() {
+        check(
+            r#"
+trait Foo {
+    //^^^
+    type T;
+    const C: usize;
+    fn f() {}
+    fn m(&self) {}
+}
+impl Foo for i32 {
+   //^^^
+    type T = i32;
+    const C: usize = 0;
+    fn f() {}
+    fn m(&self) {}
+}
+fn f<T: Foo$0>(t: T) {
+      //^^^
+    let _: T::T;
+            //^
+    t.m();
+    //^
+    T::C;
+     //^
+    T::f();
+     //^
+}
+
+fn f2<T: Foo>(t: T) {
+       //^^^
+    let _: T::T;
+    t.m();
+    T::C;
+    T::f();
+}
+"#,
+        );
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/ide/src/hover.rs b/src/tools/rust-analyzer/crates/ide/src/hover.rs
index 64b2221bdea..5ef6ac94807 100644
--- a/src/tools/rust-analyzer/crates/ide/src/hover.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/hover.rs
@@ -6,7 +6,7 @@ mod tests;
 use std::iter;
 
 use either::Either;
-use hir::{HasSource, Semantics};
+use hir::{db::DefDatabase, HasSource, LangItem, Semantics};
 use ide_db::{
     base_db::FileRange,
     defs::{Definition, IdentClass, OperatorClass},
@@ -27,10 +27,25 @@ use crate::{
 #[derive(Clone, Debug, PartialEq, Eq)]
 pub struct HoverConfig {
     pub links_in_hover: bool,
+    pub memory_layout: Option<MemoryLayoutHoverConfig>,
     pub documentation: bool,
     pub keywords: bool,
     pub format: HoverDocFormat,
-    pub interpret_tests: bool,
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub struct MemoryLayoutHoverConfig {
+    pub size: Option<MemoryLayoutHoverRenderKind>,
+    pub offset: Option<MemoryLayoutHoverRenderKind>,
+    pub alignment: Option<MemoryLayoutHoverRenderKind>,
+    pub niches: bool,
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub enum MemoryLayoutHoverRenderKind {
+    Decimal,
+    Hexadecimal,
+    Both,
 }
 
 #[derive(Clone, Debug, PartialEq, Eq)]
@@ -56,7 +71,7 @@ impl HoverAction {
                     mod_path: render::path(
                         db,
                         it.module(db)?,
-                        it.name(db).map(|name| name.to_string()),
+                        it.name(db).map(|name| name.display(db).to_string()),
                     ),
                     nav: it.try_to_nav(db)?,
                 })
@@ -119,8 +134,8 @@ fn hover_simple(
         | T![crate]
         | T![Self]
         | T![_] => 4,
-        // index and prefix ops
-        T!['['] | T![']'] | T![?] | T![*] | T![-] | T![!] => 3,
+        // index and prefix ops and closure pipe
+        T!['['] | T![']'] | T![?] | T![*] | T![-] | T![!] | T![|] => 3,
         kind if kind.is_keyword() => 2,
         T!['('] | T![')'] => 2,
         kind if kind.is_trivia() => 0,
@@ -219,6 +234,16 @@ fn hover_simple(
                 };
                 render::type_info_of(sema, config, &Either::Left(call_expr))
             })
+        })
+        // try closure
+        .or_else(|| {
+            descended().find_map(|token| {
+                if token.kind() != T![|] {
+                    return None;
+                }
+                let c = token.parent().and_then(|x| x.parent()).and_then(ast::ClosureExpr::cast)?;
+                render::closure_expr(sema, config, c)
+            })
         });
 
     result.map(|mut res: HoverResult| {
@@ -344,7 +369,14 @@ fn goto_type_action_for_def(db: &RootDatabase, def: Definition) -> Option<HoverA
     };
 
     if let Definition::GenericParam(hir::GenericParam::TypeParam(it)) = def {
-        it.trait_bounds(db).into_iter().for_each(|it| push_new_def(it.into()));
+        let krate = it.module(db).krate();
+        let sized_trait =
+            db.lang_item(krate.into(), LangItem::Sized).and_then(|lang_item| lang_item.as_trait());
+
+        it.trait_bounds(db)
+            .into_iter()
+            .filter(|&it| Some(it.into()) != sized_trait)
+            .for_each(|it| push_new_def(it.into()));
     } else {
         let ty = match def {
             Definition::Local(it) => it.ty(db),
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 da725ce502b..1362146413e 100644
--- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs
@@ -3,8 +3,8 @@ use std::fmt::Display;
 
 use either::Either;
 use hir::{
-    db::DefDatabase, Adt, AsAssocItem, AttributeTemplate, HasAttrs, HasSource, HirDisplay,
-    MirEvalError, Semantics, TypeInfo,
+    Adt, AsAssocItem, AttributeTemplate, CaptureKind, HasAttrs, HasSource, HirDisplay, Layout,
+    LayoutError, Semantics, TypeInfo,
 };
 use ide_db::{
     base_db::SourceDatabase,
@@ -27,7 +27,8 @@ use syntax::{
 use crate::{
     doc_links::{remove_links, rewrite_links},
     hover::walk_and_push_ty,
-    HoverAction, HoverConfig, HoverResult, Markup,
+    HoverAction, HoverConfig, HoverResult, Markup, MemoryLayoutHoverConfig,
+    MemoryLayoutHoverRenderKind,
 };
 
 pub(super) fn type_info_of(
@@ -35,11 +36,20 @@ pub(super) fn type_info_of(
     _config: &HoverConfig,
     expr_or_pat: &Either<ast::Expr, ast::Pat>,
 ) -> Option<HoverResult> {
-    let TypeInfo { original, adjusted } = match expr_or_pat {
+    let ty_info = match expr_or_pat {
         Either::Left(expr) => sema.type_of_expr(expr)?,
         Either::Right(pat) => sema.type_of_pat(pat)?,
     };
-    type_info(sema, _config, original, adjusted)
+    type_info(sema, _config, ty_info)
+}
+
+pub(super) fn closure_expr(
+    sema: &Semantics<'_, RootDatabase>,
+    config: &HoverConfig,
+    c: ast::ClosureExpr,
+) -> Option<HoverResult> {
+    let TypeInfo { original, .. } = sema.type_of_expr(&c.into())?;
+    closure_ty(sema, config, &TypeInfo { original, adjusted: None })
 }
 
 pub(super) fn try_expr(
@@ -361,7 +371,7 @@ fn definition_owner_name(db: &RootDatabase, def: &Definition) -> Option<String>
         Definition::Variant(e) => Some(e.parent_enum(db).name(db)),
         _ => None,
     }
-    .map(|name| name.to_string())
+    .map(|name| name.display(db).to_string())
 }
 
 pub(super) fn path(db: &RootDatabase, module: hir::Module, item_name: Option<String>) -> String {
@@ -371,7 +381,7 @@ pub(super) fn path(db: &RootDatabase, module: hir::Module, item_name: Option<Str
         .path_to_root(db)
         .into_iter()
         .rev()
-        .flat_map(|it| it.name(db).map(|name| name.to_string()));
+        .flat_map(|it| it.name(db).map(|name| name.display(db).to_string()));
     crate_name.into_iter().chain(module_path).chain(item_name).join("::")
 }
 
@@ -384,53 +394,46 @@ pub(super) fn definition(
     let mod_path = definition_mod_path(db, &def);
     let (label, docs) = match def {
         Definition::Macro(it) => label_and_docs(db, it),
-        Definition::Field(it) => label_and_layout_info_and_docs(db, it, |&it| {
-            let var_def = it.parent_def(db);
-            let id = it.index();
-            let layout = it.layout(db).ok()?;
-            let offset = match var_def {
-                hir::VariantDef::Struct(s) => Adt::from(s)
-                    .layout(db)
-                    .ok()
-                    .map(|layout| format!(", offset = {}", layout.fields.offset(id).bytes())),
-                _ => None,
-            };
-            Some(format!(
-                "size = {}, align = {}{}",
-                layout.size.bytes(),
-                layout.align.abi.bytes(),
-                offset.as_deref().unwrap_or_default()
-            ))
-        }),
-        Definition::Module(it) => label_and_docs(db, it),
-        Definition::Function(it) => label_and_layout_info_and_docs(db, it, |_| {
-            if !config.interpret_tests {
-                return None;
-            }
-            match it.eval(db) {
-                Ok(()) => Some("pass".into()),
-                Err(MirEvalError::Panic) => Some("fail".into()),
-                Err(MirEvalError::MirLowerError(f, e)) => {
-                    let name = &db.function_data(f).name;
-                    Some(format!("error: fail to lower {name} due {e:?}"))
+        Definition::Field(it) => label_and_layout_info_and_docs(
+            db,
+            it,
+            config,
+            |&it| it.layout(db),
+            |_| {
+                let var_def = it.parent_def(db);
+                let id = it.index();
+                match var_def {
+                    hir::VariantDef::Struct(s) => {
+                        Adt::from(s).layout(db).ok().and_then(|layout| layout.field_offset(id))
+                    }
+                    _ => None,
                 }
-                Err(e) => Some(format!("error: {e:?}")),
-            }
-        }),
-        Definition::Adt(it) => label_and_layout_info_and_docs(db, it, |&it| {
-            let layout = it.layout(db).ok()?;
-            Some(format!("size = {}, align = {}", layout.size.bytes(), layout.align.abi.bytes()))
-        }),
-        Definition::Variant(it) => label_value_and_docs(db, it, |&it| {
-            if !it.parent_enum(db).is_data_carrying(db) {
-                match it.eval(db) {
-                    Ok(x) => Some(if x >= 10 { format!("{x} ({x:#X})") } else { format!("{x}") }),
-                    Err(_) => it.value(db).map(|x| format!("{x:?}")),
+            },
+        ),
+        Definition::Module(it) => label_and_docs(db, it),
+        Definition::Function(it) => label_and_docs(db, it),
+        Definition::Adt(it) => {
+            label_and_layout_info_and_docs(db, it, config, |&it| it.layout(db), |_| None)
+        }
+        Definition::Variant(it) => label_value_and_layout_info_and_docs(
+            db,
+            it,
+            config,
+            |&it| {
+                if !it.parent_enum(db).is_data_carrying(db) {
+                    match it.eval(db) {
+                        Ok(x) => {
+                            Some(if x >= 10 { format!("{x} ({x:#X})") } else { format!("{x}") })
+                        }
+                        Err(_) => it.value(db).map(|x| format!("{x:?}")),
+                    }
+                } else {
+                    None
                 }
-            } else {
-                None
-            }
-        }),
+            },
+            |it| it.layout(db),
+            |layout| layout.enum_tag_size(),
+        ),
         Definition::Const(it) => label_value_and_docs(db, it, |it| {
             let body = it.render_eval(db);
             match body {
@@ -455,22 +458,26 @@ pub(super) fn definition(
         }),
         Definition::Trait(it) => label_and_docs(db, it),
         Definition::TraitAlias(it) => label_and_docs(db, it),
-        Definition::TypeAlias(it) => label_and_docs(db, it),
+        Definition::TypeAlias(it) => {
+            label_and_layout_info_and_docs(db, it, config, |&it| it.ty(db).layout(db), |_| None)
+        }
         Definition::BuiltinType(it) => {
             return famous_defs
                 .and_then(|fd| builtin(fd, it))
-                .or_else(|| Some(Markup::fenced_block(&it.name())))
+                .or_else(|| Some(Markup::fenced_block(&it.name().display(db))))
         }
-        Definition::Local(it) => return local(db, it),
+        Definition::Local(it) => return local(db, it, config),
         Definition::SelfType(impl_def) => {
             impl_def.self_ty(db).as_adt().map(|adt| label_and_docs(db, adt))?
         }
         Definition::GenericParam(it) => label_and_docs(db, it),
-        Definition::Label(it) => return Some(Markup::fenced_block(&it.name(db))),
+        Definition::Label(it) => return Some(Markup::fenced_block(&it.name(db).display(db))),
         // FIXME: We should be able to show more info about these
         Definition::BuiltinAttr(it) => return render_builtin_attr(db, it),
         Definition::ToolModule(it) => return Some(Markup::fenced_block(&it.name(db))),
-        Definition::DeriveHelper(it) => (format!("derive_helper {}", it.name(db)), None),
+        Definition::DeriveHelper(it) => {
+            (format!("derive_helper {}", it.name(db).display(db)), None)
+        }
     };
 
     let docs = docs
@@ -490,10 +497,13 @@ pub(super) fn definition(
 
 fn type_info(
     sema: &Semantics<'_, RootDatabase>,
-    _config: &HoverConfig,
-    original: hir::Type,
-    adjusted: Option<hir::Type>,
+    config: &HoverConfig,
+    ty: TypeInfo,
 ) -> Option<HoverResult> {
+    if let Some(res) = closure_ty(sema, config, &ty) {
+        return Some(res);
+    };
+    let TypeInfo { original, adjusted } = ty;
     let mut res = HoverResult::default();
     let mut targets: Vec<hir::ModuleDef> = Vec::new();
     let mut push_new_def = |item: hir::ModuleDef| {
@@ -523,6 +533,67 @@ fn type_info(
     Some(res)
 }
 
+fn closure_ty(
+    sema: &Semantics<'_, RootDatabase>,
+    config: &HoverConfig,
+    TypeInfo { original, adjusted }: &TypeInfo,
+) -> Option<HoverResult> {
+    let c = original.as_closure()?;
+    let mut captures_rendered = c.captured_items(sema.db)
+        .into_iter()
+        .map(|it| {
+            let borrow_kind = match it.kind() {
+                CaptureKind::SharedRef => "immutable borrow",
+                CaptureKind::UniqueSharedRef => "unique immutable borrow ([read more](https://doc.rust-lang.org/stable/reference/types/closure.html#unique-immutable-borrows-in-captures))",
+                CaptureKind::MutableRef => "mutable borrow",
+                CaptureKind::Move => "move",
+            };
+            format!("* `{}` by {}", it.display_place(sema.db), borrow_kind)
+        })
+        .join("\n");
+    if captures_rendered.trim().is_empty() {
+        captures_rendered = "This closure captures nothing".to_string();
+    }
+    let mut targets: Vec<hir::ModuleDef> = Vec::new();
+    let mut push_new_def = |item: hir::ModuleDef| {
+        if !targets.contains(&item) {
+            targets.push(item);
+        }
+    };
+    walk_and_push_ty(sema.db, original, &mut push_new_def);
+    c.capture_types(sema.db).into_iter().for_each(|ty| {
+        walk_and_push_ty(sema.db, &ty, &mut push_new_def);
+    });
+
+    let adjusted = if let Some(adjusted_ty) = adjusted {
+        walk_and_push_ty(sema.db, &adjusted_ty, &mut push_new_def);
+        format!(
+            "\nCoerced to: {}",
+            adjusted_ty.display(sema.db).with_closure_style(hir::ClosureStyle::ImplFn)
+        )
+    } else {
+        String::new()
+    };
+    let mut markup = format!("```rust\n{}", c.display_with_id(sema.db),);
+
+    if let Some(layout) =
+        render_memory_layout(config.memory_layout, || original.layout(sema.db), |_| None, |_| None)
+    {
+        format_to!(markup, "{layout}");
+    }
+    format_to!(
+        markup,
+        "\n{}\n```{adjusted}\n\n## Captures\n{}",
+        c.display_with_impl(sema.db),
+        captures_rendered,
+    );
+
+    let mut res = HoverResult::default();
+    res.actions.push(HoverAction::goto_type_from_targets(sema.db, targets));
+    res.markup = markup.into();
+    Some(res)
+}
+
 fn render_builtin_attr(db: &RootDatabase, attr: hir::BuiltinAttr) -> Option<Markup> {
     let name = attr.name(db);
     let desc = format!("#[{name}]");
@@ -553,21 +624,59 @@ where
     (label, docs)
 }
 
-fn label_and_layout_info_and_docs<D, E, V>(
+fn label_and_layout_info_and_docs<D, E, E2>(
+    db: &RootDatabase,
+    def: D,
+    config: &HoverConfig,
+    layout_extractor: E,
+    layout_offset_extractor: E2,
+) -> (String, Option<hir::Documentation>)
+where
+    D: HasAttrs + HirDisplay,
+    E: Fn(&D) -> Result<Layout, LayoutError>,
+    E2: Fn(&Layout) -> Option<u64>,
+{
+    let mut label = def.display(db).to_string();
+    if let Some(layout) = render_memory_layout(
+        config.memory_layout,
+        || layout_extractor(&def),
+        layout_offset_extractor,
+        |_| None,
+    ) {
+        format_to!(label, "{layout}");
+    }
+    let docs = def.attrs(db).docs();
+    (label, docs)
+}
+
+fn label_value_and_layout_info_and_docs<D, E, E2, E3, V>(
     db: &RootDatabase,
     def: D,
+    config: &HoverConfig,
     value_extractor: E,
+    layout_extractor: E2,
+    layout_tag_extractor: E3,
 ) -> (String, Option<hir::Documentation>)
 where
     D: HasAttrs + HirDisplay,
     E: Fn(&D) -> Option<V>,
+    E2: Fn(&D) -> Result<Layout, LayoutError>,
+    E3: Fn(&Layout) -> Option<usize>,
     V: Display,
 {
-    let label = if let Some(value) = value_extractor(&def) {
-        format!("{} // {value}", def.display(db))
-    } else {
-        def.display(db).to_string()
+    let value = value_extractor(&def);
+    let mut label = match value {
+        Some(value) => format!("{} = {value}", def.display(db)),
+        None => def.display(db).to_string(),
     };
+    if let Some(layout) = render_memory_layout(
+        config.memory_layout,
+        || layout_extractor(&def),
+        |_| None,
+        layout_tag_extractor,
+    ) {
+        format_to!(label, "{layout}");
+    }
     let docs = def.attrs(db).docs();
     (label, docs)
 }
@@ -616,26 +725,26 @@ fn markup(docs: Option<String>, desc: String, mod_path: Option<String>) -> Optio
 
 fn builtin(famous_defs: &FamousDefs<'_, '_>, builtin: hir::BuiltinType) -> Option<Markup> {
     // std exposes prim_{} modules with docstrings on the root to document the builtins
-    let primitive_mod = format!("prim_{}", builtin.name());
+    let primitive_mod = format!("prim_{}", builtin.name().display(famous_defs.0.db));
     let doc_owner = find_std_module(famous_defs, &primitive_mod)?;
     let docs = doc_owner.attrs(famous_defs.0.db).docs()?;
-    markup(Some(docs.into()), builtin.name().to_string(), None)
+    markup(Some(docs.into()), builtin.name().display(famous_defs.0.db).to_string(), None)
 }
 
 fn find_std_module(famous_defs: &FamousDefs<'_, '_>, name: &str) -> Option<hir::Module> {
     let db = famous_defs.0.db;
     let std_crate = famous_defs.std()?;
     let std_root_module = std_crate.root_module(db);
-    std_root_module
-        .children(db)
-        .find(|module| module.name(db).map_or(false, |module| module.to_string() == name))
+    std_root_module.children(db).find(|module| {
+        module.name(db).map_or(false, |module| module.display(db).to_string() == name)
+    })
 }
 
-fn local(db: &RootDatabase, it: hir::Local) -> Option<Markup> {
+fn local(db: &RootDatabase, it: hir::Local, config: &HoverConfig) -> Option<Markup> {
     let ty = it.ty(db);
     let ty = ty.display_truncated(db, None);
     let is_mut = if it.is_mut(db) { "mut " } else { "" };
-    let desc = match it.primary_source(db).into_ident_pat() {
+    let mut desc = match it.primary_source(db).into_ident_pat() {
         Some(ident) => {
             let name = it.name(db);
             let let_kw = if ident
@@ -647,13 +756,91 @@ fn local(db: &RootDatabase, it: hir::Local) -> Option<Markup> {
             } else {
                 ""
             };
-            format!("{let_kw}{is_mut}{name}: {ty}")
+            format!("{let_kw}{is_mut}{}: {ty}", name.display(db))
         }
         None => format!("{is_mut}self: {ty}"),
     };
+    if let Some(layout) =
+        render_memory_layout(config.memory_layout, || it.ty(db).layout(db), |_| None, |_| None)
+    {
+        format_to!(desc, "{layout}");
+    }
     markup(None, desc, None)
 }
 
+fn render_memory_layout(
+    config: Option<MemoryLayoutHoverConfig>,
+    layout: impl FnOnce() -> Result<Layout, LayoutError>,
+    offset: impl FnOnce(&Layout) -> Option<u64>,
+    tag: impl FnOnce(&Layout) -> Option<usize>,
+) -> Option<String> {
+    // field
+
+    let config = config?;
+    let layout = layout().ok()?;
+
+    let mut label = String::from(" // ");
+
+    if let Some(render) = config.size {
+        let size = match tag(&layout) {
+            Some(tag) => layout.size() as usize - tag,
+            None => layout.size() as usize,
+        };
+        format_to!(label, "size = ");
+        match render {
+            MemoryLayoutHoverRenderKind::Decimal => format_to!(label, "{size}"),
+            MemoryLayoutHoverRenderKind::Hexadecimal => format_to!(label, "{size:#X}"),
+            MemoryLayoutHoverRenderKind::Both if size >= 10 => {
+                format_to!(label, "{size} ({size:#X})")
+            }
+            MemoryLayoutHoverRenderKind::Both => format_to!(label, "{size}"),
+        }
+        format_to!(label, ", ");
+    }
+
+    if let Some(render) = config.alignment {
+        let align = layout.align();
+        format_to!(label, "align = ");
+        match render {
+            MemoryLayoutHoverRenderKind::Decimal => format_to!(label, "{align}",),
+            MemoryLayoutHoverRenderKind::Hexadecimal => format_to!(label, "{align:#X}",),
+            MemoryLayoutHoverRenderKind::Both if align >= 10 => {
+                format_to!(label, "{align} ({align:#X})")
+            }
+            MemoryLayoutHoverRenderKind::Both => {
+                format_to!(label, "{align}")
+            }
+        }
+        format_to!(label, ", ");
+    }
+
+    if let Some(render) = config.offset {
+        if let Some(offset) = offset(&layout) {
+            format_to!(label, "offset = ");
+            match render {
+                MemoryLayoutHoverRenderKind::Decimal => format_to!(label, "{offset}"),
+                MemoryLayoutHoverRenderKind::Hexadecimal => format_to!(label, "{offset:#X}"),
+                MemoryLayoutHoverRenderKind::Both if offset >= 10 => {
+                    format_to!(label, "{offset} ({offset:#X})")
+                }
+                MemoryLayoutHoverRenderKind::Both => {
+                    format_to!(label, "{offset}")
+                }
+            }
+            format_to!(label, ", ");
+        }
+    }
+
+    if config.niches {
+        if let Some(niches) = layout.niches() {
+            format_to!(label, "niches = {niches}, ");
+        }
+    }
+    label.pop(); // ' '
+    label.pop(); // ','
+    Some(label)
+}
+
 struct KeywordHint {
     description: String,
     keyword_mod: String,
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 57bf0f9ad5f..a2f96977581 100644
--- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
@@ -2,14 +2,21 @@ use expect_test::{expect, Expect};
 use ide_db::base_db::{FileLoader, FileRange};
 use syntax::TextRange;
 
-use crate::{fixture, HoverConfig, HoverDocFormat};
+use crate::{
+    fixture, HoverConfig, HoverDocFormat, MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind,
+};
 
 const HOVER_BASE_CONFIG: HoverConfig = HoverConfig {
     links_in_hover: false,
+    memory_layout: Some(MemoryLayoutHoverConfig {
+        size: Some(MemoryLayoutHoverRenderKind::Both),
+        offset: Some(MemoryLayoutHoverRenderKind::Both),
+        alignment: Some(MemoryLayoutHoverRenderKind::Both),
+        niches: true,
+    }),
     documentation: true,
     format: HoverDocFormat::Markdown,
     keywords: true,
-    interpret_tests: false,
 };
 
 fn check_hover_no_result(ra_fixture: &str) {
@@ -58,6 +65,23 @@ fn check_hover_no_links(ra_fixture: &str, expect: Expect) {
     expect.assert_eq(&actual)
 }
 
+fn check_hover_no_memory_layout(ra_fixture: &str, expect: Expect) {
+    let (analysis, position) = fixture::position(ra_fixture);
+    let hover = analysis
+        .hover(
+            &HoverConfig { memory_layout: None, ..HOVER_BASE_CONFIG },
+            FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) },
+        )
+        .unwrap()
+        .unwrap();
+
+    let content = analysis.db.file_text(position.file_id);
+    let hovered_element = &content[hover.range];
+
+    let actual = format!("*{hovered_element}*\n{}\n", hover.info.markup);
+    expect.assert_eq(&actual)
+}
+
 fn check_hover_no_markdown(ra_fixture: &str, expect: Expect) {
     let (analysis, position) = fixture::position(ra_fixture);
     let hover = analysis
@@ -97,6 +121,15 @@ fn check_hover_range(ra_fixture: &str, expect: Expect) {
     expect.assert_eq(hover.info.markup.as_str())
 }
 
+fn check_hover_range_actions(ra_fixture: &str, expect: Expect) {
+    let (analysis, range) = fixture::range(ra_fixture);
+    let hover = analysis
+        .hover(&HoverConfig { links_in_hover: true, ..HOVER_BASE_CONFIG }, range)
+        .unwrap()
+        .unwrap();
+    expect.assert_debug_eq(&hover.info.actions);
+}
+
 fn check_hover_range_no_results(ra_fixture: &str) {
     let (analysis, range) = fixture::range(ra_fixture);
     let hover = analysis.hover(&HOVER_BASE_CONFIG, range).unwrap();
@@ -124,7 +157,7 @@ fn foo() {
             *local*
 
             ```rust
-            let local: i32
+            let local: i32 // size = 4, align = 4
             ```
         "#]],
     );
@@ -199,6 +232,181 @@ fn main() {
 }
 
 #[test]
+fn hover_closure() {
+    check(
+        r#"
+//- minicore: copy
+fn main() {
+    let x = 2;
+    let y = $0|z| x + z;
+}
+"#,
+        expect![[r#"
+            *|*
+            ```rust
+            {closure#0} // size = 8, align = 8, niches = 1
+            impl Fn(i32) -> i32
+            ```
+
+            ## Captures
+            * `x` by immutable borrow
+        "#]],
+    );
+
+    check(
+        r#"
+//- minicore: copy
+fn foo(x: impl Fn(i32) -> i32) {
+
+}
+fn main() {
+    foo($0|x: i32| x)
+}
+"#,
+        expect![[r#"
+            *|*
+            ```rust
+            {closure#0} // size = 0, align = 1
+            impl Fn(i32) -> i32
+            ```
+
+            ## Captures
+            This closure captures nothing
+        "#]],
+    );
+
+    check(
+        r#"
+//- minicore: copy
+
+struct Z { f: i32 }
+
+struct Y(&'static mut Z)
+
+struct X {
+    f1: Y,
+    f2: (Y, Y),
+}
+
+fn main() {
+    let x: X;
+    let y = $0|| {
+        x.f1;
+        &mut x.f2.0 .0.f;
+    };
+}
+"#,
+        expect![[r#"
+            *|*
+            ```rust
+            {closure#0} // size = 16 (0x10), align = 8, niches = 1
+            impl FnOnce()
+            ```
+
+            ## Captures
+            * `x.f1` by move
+            * `(*x.f2.0.0).f` by mutable borrow
+        "#]],
+    );
+    check(
+        r#"
+//- minicore: copy, option
+
+fn do_char(c: char) {}
+
+fn main() {
+    let x = None;
+    let y = |$0| {
+        match x {
+            Some(c) => do_char(c),
+            None => x = None,
+        }
+    };
+}
+"#,
+        expect![[r#"
+            *|*
+            ```rust
+            {closure#0} // size = 8, align = 8, niches = 1
+            impl FnMut()
+            ```
+
+            ## Captures
+            * `x` by mutable borrow
+        "#]],
+    );
+}
+
+#[test]
+fn hover_ranged_closure() {
+    check_hover_range(
+        r#"
+//- minicore: fn
+struct S;
+struct S2;
+fn main() {
+    let x = &S;
+    let y = ($0|| {x; S2}$0).call();
+}
+"#,
+        expect![[r#"
+            ```rust
+            {closure#0} // size = 8, align = 8, niches = 1
+            impl FnOnce() -> S2
+            ```
+            Coerced to: &impl FnOnce() -> S2
+
+            ## Captures
+            * `x` by move"#]],
+    );
+    check_hover_range_actions(
+        r#"
+//- minicore: fn
+struct S;
+struct S2;
+fn main() {
+    let x = &S;
+    let y = ($0|| {x; S2}$0).call();
+}
+"#,
+        expect![[r#"
+            [
+                GoToType(
+                    [
+                        HoverGotoTypeData {
+                            mod_path: "test::S2",
+                            nav: NavigationTarget {
+                                file_id: FileId(
+                                    0,
+                                ),
+                                full_range: 10..20,
+                                focus_range: 17..19,
+                                name: "S2",
+                                kind: Struct,
+                                description: "struct S2",
+                            },
+                        },
+                        HoverGotoTypeData {
+                            mod_path: "test::S",
+                            nav: NavigationTarget {
+                                file_id: FileId(
+                                    0,
+                                ),
+                                full_range: 0..9,
+                                focus_range: 7..8,
+                                name: "S",
+                                kind: Struct,
+                                description: "struct S",
+                            },
+                        },
+                    ],
+                ),
+            ]
+        "#]],
+    );
+}
+
+#[test]
 fn hover_shows_long_type_of_an_expression() {
     check(
         r#"
@@ -222,12 +430,12 @@ fn main() {
 }
 "#,
         expect![[r#"
-                *iter*
+            *iter*
 
-                ```rust
-                let mut iter: Iter<Scan<OtherStruct<OtherStruct<i32>>, |&mut u32, &u32, &mut u32| -> Option<u32>, u32>>
-                ```
-            "#]],
+            ```rust
+            let mut iter: Iter<Scan<OtherStruct<OtherStruct<i32>>, impl Fn(&mut u32, &u32, &mut u32) -> Option<u32>, u32>> // size = 8, align = 4
+            ```
+        "#]],
     );
 }
 
@@ -604,12 +812,12 @@ fn main() {
     let zz$0 = Test { t: 23u8, k: 33 };
 }"#,
         expect![[r#"
-                *zz*
+            *zz*
 
-                ```rust
-                let zz: Test<i32>
-                ```
-            "#]],
+            ```rust
+            let zz: Test<i32> // size = 8, align = 4
+            ```
+        "#]],
     );
     check_hover_range(
         r#"
@@ -655,12 +863,12 @@ use Option::Some;
 fn main() { let b$0ar = Some(12); }
 "#,
         expect![[r#"
-                *bar*
+            *bar*
 
-                ```rust
-                let bar: Option<i32>
-                ```
-            "#]],
+            ```rust
+            let bar: Option<i32> // size = 4, align = 4
+            ```
+        "#]],
     );
 }
 
@@ -724,12 +932,12 @@ fn hover_for_local_variable() {
     check(
         r#"fn func(foo: i32) { fo$0o; }"#,
         expect![[r#"
-                *foo*
+            *foo*
 
-                ```rust
-                foo: i32
-                ```
-            "#]],
+            ```rust
+            foo: i32 // size = 4, align = 4
+            ```
+        "#]],
     )
 }
 
@@ -738,12 +946,12 @@ fn hover_for_local_variable_pat() {
     check(
         r#"fn func(fo$0o: i32) {}"#,
         expect![[r#"
-                *foo*
+            *foo*
 
-                ```rust
-                foo: i32
-                ```
-            "#]],
+            ```rust
+            foo: i32 // size = 4, align = 4
+            ```
+        "#]],
     )
 }
 
@@ -752,12 +960,12 @@ fn hover_local_var_edge() {
     check(
         r#"fn func(foo: i32) { if true { $0foo; }; }"#,
         expect![[r#"
-                *foo*
+            *foo*
 
-                ```rust
-                foo: i32
-                ```
-            "#]],
+            ```rust
+            foo: i32 // size = 4, align = 4
+            ```
+        "#]],
     )
 }
 
@@ -766,12 +974,12 @@ fn hover_for_param_edge() {
     check(
         r#"fn func($0foo: i32) {}"#,
         expect![[r#"
-                *foo*
+            *foo*
 
-                ```rust
-                foo: i32
-                ```
-            "#]],
+            ```rust
+            foo: i32 // size = 4, align = 4
+            ```
+        "#]],
     )
 }
 
@@ -810,12 +1018,12 @@ impl Thing {
 fn main() { let foo_$0test = Thing::new(); }
 "#,
         expect![[r#"
-                *foo_test*
+            *foo_test*
 
-                ```rust
-                let foo_test: Thing
-                ```
-            "#]],
+            ```rust
+            let foo_test: Thing // size = 4, align = 4
+            ```
+        "#]],
     )
 }
 
@@ -970,12 +1178,12 @@ fn y() {
 }
 "#,
         expect![[r#"
-                *x*
+            *x*
 
-                ```rust
-                let x: i32
-                ```
-            "#]],
+            ```rust
+            let x: i32 // size = 4, align = 4
+            ```
+        "#]],
     )
 }
 
@@ -1100,12 +1308,12 @@ macro_rules! id { ($($tt:tt)*) => { $($tt)* } }
 fn foo(bar:u32) { let a = id!(ba$0r); }
 "#,
         expect![[r#"
-                *bar*
+            *bar*
 
-                ```rust
-                bar: u32
-                ```
-            "#]],
+            ```rust
+            bar: u32 // size = 4, align = 4
+            ```
+        "#]],
     );
 }
 
@@ -1118,12 +1326,12 @@ macro_rules! id { ($($tt:tt)*) => { id_deep!($($tt)*) } }
 fn foo(bar:u32) { let a = id!(ba$0r); }
 "#,
         expect![[r#"
-                *bar*
+            *bar*
 
-                ```rust
-                bar: u32
-                ```
-            "#]],
+            ```rust
+            bar: u32 // size = 4, align = 4
+            ```
+        "#]],
     );
 }
 
@@ -1320,16 +1528,16 @@ fn test_hover_function_pointer_show_identifiers() {
     check(
         r#"type foo$0 = fn(a: i32, b: i32) -> i32;"#,
         expect![[r#"
-                *foo*
+            *foo*
 
-                ```rust
-                test
-                ```
+            ```rust
+            test
+            ```
 
-                ```rust
-                type foo = fn(a: i32, b: i32) -> i32
-                ```
-            "#]],
+            ```rust
+            type foo = fn(a: i32, b: i32) -> i32 // size = 8, align = 8, niches = 1
+            ```
+        "#]],
     );
 }
 
@@ -1338,16 +1546,16 @@ fn test_hover_function_pointer_no_identifier() {
     check(
         r#"type foo$0 = fn(i32, _: i32) -> i32;"#,
         expect![[r#"
-                *foo*
+            *foo*
 
-                ```rust
-                test
-                ```
+            ```rust
+            test
+            ```
 
-                ```rust
-                type foo = fn(i32, i32) -> i32
-                ```
-            "#]],
+            ```rust
+            type foo = fn(i32, i32) -> i32 // size = 8, align = 8, niches = 1
+            ```
+        "#]],
     );
 }
 
@@ -1668,6 +1876,86 @@ pub fn fo$0o() {}
 }
 
 #[test]
+fn test_hover_layout_of_variant() {
+    check(
+        r#"enum Foo {
+            Va$0riant1(u8, u16),
+            Variant2(i32, u8, i64),
+        }"#,
+        expect![[r#"
+            *Variant1*
+
+            ```rust
+            test::Foo
+            ```
+
+            ```rust
+            Variant1(u8, u16) // size = 4, align = 2
+            ```
+        "#]],
+    );
+}
+
+#[test]
+fn test_hover_layout_of_enum() {
+    check(
+        r#"enum $0Foo {
+            Variant1(u8, u16),
+            Variant2(i32, u8, i64),
+        }"#,
+        expect![[r#"
+            *Foo*
+
+            ```rust
+            test
+            ```
+
+            ```rust
+            enum Foo // size = 16 (0x10), align = 8, niches = 254
+            ```
+        "#]],
+    );
+}
+
+#[test]
+fn test_hover_no_memory_layout() {
+    check_hover_no_memory_layout(
+        r#"struct Foo { fiel$0d_a: u8, field_b: i32, field_c: i16 }"#,
+        expect![[r#"
+            *field_a*
+
+            ```rust
+            test::Foo
+            ```
+
+            ```rust
+            field_a: u8
+            ```
+        "#]],
+    );
+
+    check_hover_no_memory_layout(
+        r#"
+//- minicore: copy
+fn main() {
+    let x = 2;
+    let y = $0|z| x + z;
+}
+"#,
+        expect![[r#"
+            *|*
+            ```rust
+            {closure#0}
+            impl Fn(i32) -> i32
+            ```
+
+            ## Captures
+            * `x` by immutable borrow
+        "#]],
+    );
+}
+
+#[test]
 fn test_hover_macro_generated_struct_fn_doc_comment() {
     cov_mark::check!(hover_macro_generated_struct_fn_doc_comment);
 
@@ -2021,6 +2309,19 @@ fn main() { let s$0t = S{ f1:Arg(0) }; }
 }
 
 #[test]
+fn test_hover_generic_excludes_sized_go_to_action() {
+    check_actions(
+        r#"
+//- minicore: sized
+struct S<T$0>(T);
+    "#,
+        expect![[r#"
+            []
+        "#]],
+    );
+}
+
+#[test]
 fn test_hover_generic_struct_has_flattened_goto_type_actions() {
     check_actions(
         r#"
@@ -2079,52 +2380,53 @@ mod M {
 fn main() { let s$0t = (A(1), B(2), M::C(3) ); }
 "#,
         expect![[r#"
-                [
-                    GoToType(
-                        [
-                            HoverGotoTypeData {
-                                mod_path: "test::A",
-                                nav: NavigationTarget {
-                                    file_id: FileId(
-                                        0,
-                                    ),
-                                    full_range: 0..14,
-                                    focus_range: 7..8,
-                                    name: "A",
-                                    kind: Struct,
-                                    description: "struct A",
-                                },
+            [
+                GoToType(
+                    [
+                        HoverGotoTypeData {
+                            mod_path: "test::A",
+                            nav: NavigationTarget {
+                                file_id: FileId(
+                                    0,
+                                ),
+                                full_range: 0..14,
+                                focus_range: 7..8,
+                                name: "A",
+                                kind: Struct,
+                                description: "struct A",
                             },
-                            HoverGotoTypeData {
-                                mod_path: "test::B",
-                                nav: NavigationTarget {
-                                    file_id: FileId(
-                                        0,
-                                    ),
-                                    full_range: 15..29,
-                                    focus_range: 22..23,
-                                    name: "B",
-                                    kind: Struct,
-                                    description: "struct B",
-                                },
+                        },
+                        HoverGotoTypeData {
+                            mod_path: "test::B",
+                            nav: NavigationTarget {
+                                file_id: FileId(
+                                    0,
+                                ),
+                                full_range: 15..29,
+                                focus_range: 22..23,
+                                name: "B",
+                                kind: Struct,
+                                description: "struct B",
                             },
-                            HoverGotoTypeData {
-                                mod_path: "test::M::C",
-                                nav: NavigationTarget {
-                                    file_id: FileId(
-                                        0,
-                                    ),
-                                    full_range: 42..60,
-                                    focus_range: 53..54,
-                                    name: "C",
-                                    kind: Struct,
-                                    description: "pub struct C",
-                                },
+                        },
+                        HoverGotoTypeData {
+                            mod_path: "test::M::C",
+                            nav: NavigationTarget {
+                                file_id: FileId(
+                                    0,
+                                ),
+                                full_range: 42..60,
+                                focus_range: 53..54,
+                                name: "C",
+                                kind: Struct,
+                                container_name: "M",
+                                description: "pub struct C",
                             },
-                        ],
-                    ),
-                ]
-            "#]],
+                        },
+                    ],
+                ),
+            ]
+        "#]],
     );
 }
 
@@ -2453,6 +2755,7 @@ pub mod future {
                                 focus_range: 60..66,
                                 name: "Future",
                                 kind: Trait,
+                                container_name: "future",
                                 description: "pub trait Future",
                             },
                         },
@@ -2908,7 +3211,7 @@ fn main() {
             *f*
 
             ```rust
-            f: &i32
+            f: &i32 // size = 8, align = 8, niches = 1
             ```
             ---
 
@@ -2958,7 +3261,7 @@ fn main() {
             *value*
 
             ```rust
-            let value: Const<1>
+            let value: Const<1> // size = 0, align = 1
             ```
         "#]],
     );
@@ -2978,7 +3281,7 @@ fn main() {
             *value*
 
             ```rust
-            let value: Const<0>
+            let value: Const<0> // size = 0, align = 1
             ```
         "#]],
     );
@@ -2998,7 +3301,7 @@ fn main() {
             *value*
 
             ```rust
-            let value: Const<-1>
+            let value: Const<-1> // size = 0, align = 1
             ```
         "#]],
     );
@@ -3018,7 +3321,7 @@ fn main() {
             *value*
 
             ```rust
-            let value: Const<true>
+            let value: Const<true> // size = 0, align = 1
             ```
         "#]],
     );
@@ -3038,7 +3341,7 @@ fn main() {
             *value*
 
             ```rust
-            let value: Const<'🦀'>
+            let value: Const<'🦀'> // size = 0, align = 1
             ```
         "#]],
     );
@@ -3054,12 +3357,12 @@ impl Foo {
 }
 "#,
         expect![[r#"
-                *self*
+            *self*
 
-                ```rust
-                self: &Foo
-                ```
-            "#]],
+            ```rust
+            self: &Foo // size = 8, align = 8, niches = 1
+            ```
+        "#]],
     );
 }
 
@@ -3074,12 +3377,12 @@ impl Foo {
 }
 "#,
         expect![[r#"
-                *self*
+            *self*
 
-                ```rust
-                self: Arc<Foo>
-                ```
-            "#]],
+            ```rust
+            self: Arc<Foo> // size = 0, align = 1
+            ```
+        "#]],
     );
 }
 
@@ -3115,7 +3418,7 @@ mod Foo$0 {
 }
 
 #[test]
-fn hover_doc_outer_inner_attribue() {
+fn hover_doc_outer_inner_attribute() {
     check(
         r#"
 #[doc = "Be quick;"]
@@ -3146,7 +3449,7 @@ mod Foo$0 {
 }
 
 #[test]
-fn hover_doc_block_style_indentend() {
+fn hover_doc_block_style_indent_end() {
     check(
         r#"
 /**
@@ -3455,16 +3758,16 @@ struct Foo<const LEN: usize>;
 type Fo$0o2 = Foo<2>;
 "#,
         expect![[r#"
-                *Foo2*
+            *Foo2*
 
-                ```rust
-                test
-                ```
+            ```rust
+            test
+            ```
 
-                ```rust
-                type Foo2 = Foo<2>
-                ```
-            "#]],
+            ```rust
+            type Foo2 = Foo<2> // size = 0, align = 1
+            ```
+        "#]],
     );
 }
 
@@ -3504,7 +3807,7 @@ enum E {
             ```
 
             ```rust
-            A = 8
+            A = 8 // size = 1, align = 1
             ```
 
             ---
@@ -3529,7 +3832,7 @@ enum E {
             ```
 
             ```rust
-            A = 12 (0xC)
+            A = 12 (0xC) // size = 1, align = 1
             ```
 
             ---
@@ -3555,7 +3858,7 @@ enum E {
             ```
 
             ```rust
-            B = 2
+            B = 2 // size = 1, align = 1
             ```
 
             ---
@@ -3581,7 +3884,7 @@ enum E {
             ```
 
             ```rust
-            B = 5
+            B = 5 // size = 1, align = 1
             ```
 
             ---
@@ -4035,6 +4338,235 @@ const FOO$0: f64 = 1.0f64;
 }
 
 #[test]
+fn hover_const_eval_floating_point() {
+    check(
+        r#"
+extern "rust-intrinsic" {
+    pub fn expf64(x: f64) -> f64;
+}
+
+const FOO$0: f64 = expf64(1.2);
+"#,
+        expect![[r#"
+            *FOO*
+
+            ```rust
+            test
+            ```
+
+            ```rust
+            const FOO: f64 = 3.3201169227365472
+            ```
+        "#]],
+    );
+}
+
+#[test]
+fn hover_const_eval_enum() {
+    check(
+        r#"
+enum Enum {
+    V1,
+    V2,
+}
+
+const VX: Enum = Enum::V1;
+
+const FOO$0: Enum = VX;
+"#,
+        expect![[r#"
+            *FOO*
+
+            ```rust
+            test
+            ```
+
+            ```rust
+            const FOO: Enum = V1
+            ```
+        "#]],
+    );
+    check(
+        r#"
+//- minicore: option
+const FOO$0: Option<i32> = Some(2);
+"#,
+        expect![[r#"
+            *FOO*
+
+            ```rust
+            test
+            ```
+
+            ```rust
+            const FOO: Option<i32> = Some(2)
+            ```
+        "#]],
+    );
+    check(
+        r#"
+//- minicore: option
+const FOO$0: Option<&i32> = Some(2).as_ref();
+"#,
+        expect![[r#"
+            *FOO*
+
+            ```rust
+            test
+            ```
+
+            ```rust
+            const FOO: Option<&i32> = Some(&2)
+            ```
+        "#]],
+    );
+}
+
+#[test]
+fn hover_const_eval_slice() {
+    check(
+        r#"
+//- minicore: slice, index, coerce_unsized
+const FOO$0: &[i32] = &[1, 2, 3 + 4];
+"#,
+        expect![[r#"
+            *FOO*
+
+            ```rust
+            test
+            ```
+
+            ```rust
+            const FOO: &[i32] = &[1, 2, 7]
+            ```
+        "#]],
+    );
+    check(
+        r#"
+//- minicore: slice, index, coerce_unsized
+const FOO$0: &[i32; 5] = &[12; 5];
+"#,
+        expect![[r#"
+            *FOO*
+
+            ```rust
+            test
+            ```
+
+            ```rust
+            const FOO: &[i32; 5] = &[12, 12, 12, 12, 12]
+            ```
+        "#]],
+    );
+    check(
+        r#"
+//- minicore: slice, index, coerce_unsized
+
+const FOO$0: (&i32, &[i32], &i32) = {
+    let a: &[i32] = &[1, 2, 3];
+    (&a[0], a, &a[0])
+}
+"#,
+        expect![[r#"
+            *FOO*
+
+            ```rust
+            test
+            ```
+
+            ```rust
+            const FOO: (&i32, &[i32], &i32) = (&1, &[1, 2, 3], &1)
+            ```
+        "#]],
+    );
+    check(
+        r#"
+//- minicore: slice, index, coerce_unsized
+
+struct Tree(&[Tree]);
+
+const FOO$0: Tree = {
+    let x = &[Tree(&[]), Tree(&[Tree(&[])])];
+    Tree(&[Tree(x), Tree(x)])
+}
+"#,
+        expect![[r#"
+            *FOO*
+
+            ```rust
+            test
+            ```
+
+            ```rust
+            const FOO: Tree = Tree(&[Tree(&[Tree(&[]), Tree(&[Tree(&[])])]), Tree(&[Tree(&[]), Tree(&[Tree(&[])])])])
+            ```
+        "#]],
+    );
+}
+
+#[test]
+fn hover_const_eval_str() {
+    check(
+        r#"
+const FOO$0: &str = "foo";
+"#,
+        expect![[r#"
+            *FOO*
+
+            ```rust
+            test
+            ```
+
+            ```rust
+            const FOO: &str = "foo"
+            ```
+        "#]],
+    );
+    check(
+        r#"
+struct X {
+    a: &'static str,
+    b: &'static str,
+}
+const FOO$0: X = X {
+    a: "axiom",
+    b: "buy N large",
+};
+"#,
+        expect![[r#"
+            *FOO*
+
+            ```rust
+            test
+            ```
+
+            ```rust
+            const FOO: X = X { a: "axiom", b: "buy N large" }
+            ```
+        "#]],
+    );
+    check(
+        r#"
+const FOO$0: (&str, &str) = {
+    let x = "foo";
+    (x, x)
+};
+"#,
+        expect![[r#"
+            *FOO*
+
+            ```rust
+            test
+            ```
+
+            ```rust
+            const FOO: (&str, &str) = ("foo", "foo")
+            ```
+        "#]],
+    );
+}
+
+#[test]
 fn hover_const_eval_in_generic_trait() {
     // Doesn't compile, but we shouldn't crash.
     check(
@@ -4115,7 +4647,7 @@ fn foo(e: E) {
             ```
 
             ```rust
-            A = 3
+            A = 3 // size = 0, align = 1
             ```
 
             ---
@@ -4137,9 +4669,9 @@ fn main() {
             *tile4*
 
             ```rust
-            let tile4: [u32; 8]
+            let tile4: [u32; 8] // size = 32 (0x20), align = 4
             ```
-            "#]],
+        "#]],
     );
 }
 
@@ -4242,7 +4774,7 @@ fn foo() {
 /// [threads]: ../book/ch16-01-threads.html#using-move-closures-with-threads
 mod move_keyword {}
 "#,
-        expect![[r##"
+        expect![[r#"
             *move*
 
             ```rust
@@ -4251,11 +4783,11 @@ mod move_keyword {}
 
             ---
 
-            [closure](https://doc.rust-lang.org/nightly/book/ch13-01-closures.html)
-            [closures](https://doc.rust-lang.org/nightly/book/ch13-01-closures.html)
-            [threads](https://doc.rust-lang.org/nightly/book/ch16-01-threads.html#using-move-closures-with-threads)
+            [closure](https://doc.rust-lang.org/stable/book/ch13-01-closures.html)
+            [closures](https://doc.rust-lang.org/stable/book/ch13-01-closures.html)
+            [threads](https://doc.rust-lang.org/stable/book/ch16-01-threads.html#using-move-closures-with-threads)
             <https://doc.rust-lang.org/nightly/book/ch13-01-closures.html>
-        "##]],
+        "#]],
     );
 }
 
@@ -4288,7 +4820,7 @@ fn hover_builtin() {
     check(
         r#"
 //- /main.rs crate:main deps:std
-cosnt _: &str$0 = ""; }
+const _: &str$0 = ""; }
 
 //- /libstd.rs crate:std
 /// Docs for prim_str
@@ -5009,7 +5541,7 @@ fn foo() {
 fn hover_try_expr_res() {
     check_hover_range(
         r#"
-//- minicore:result
+//- minicore: try, from, result
 struct FooError;
 
 fn foo() -> Result<(), FooError> {
@@ -5023,7 +5555,7 @@ fn foo() -> Result<(), FooError> {
     );
     check_hover_range(
         r#"
-//- minicore:result
+//- minicore: try, from, result
 struct FooError;
 struct BarError;
 
@@ -5044,6 +5576,7 @@ fn foo() -> Result<(), FooError> {
 fn hover_try_expr() {
     check_hover_range(
         r#"
+//- minicore: try
 struct NotResult<T, U>(T, U);
 struct Short;
 struct Looooong;
@@ -5061,6 +5594,7 @@ fn foo() -> NotResult<(), Looooong> {
     );
     check_hover_range(
         r#"
+//- minicore: try
 struct NotResult<T, U>(T, U);
 struct Short;
 struct Looooong;
@@ -5092,7 +5626,7 @@ fn foo() -> Option<()> {
 "#,
         expect![[r#"
                 ```rust
-                <Option<i32> as Try>::Output
+                i32
                 ```"#]],
     );
 }
@@ -5312,7 +5846,7 @@ enum Enum {
             ```
 
             ```rust
-            RecordV { field: u32 }
+            RecordV { field: u32 } // size = 4, align = 4
             ```
         "#]],
     );
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 ac477339ec2..29259167419 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs
@@ -5,7 +5,8 @@ use std::{
 
 use either::Either;
 use hir::{
-    known, HasVisibility, HirDisplay, HirDisplayError, HirWrite, ModuleDef, ModuleDefId, Semantics,
+    known, ClosureStyle, HasVisibility, HirDisplay, HirDisplayError, HirWrite, ModuleDef,
+    ModuleDefId, Semantics,
 };
 use ide_db::{base_db::FileRange, famous_defs::FamousDefs, RootDatabase};
 use itertools::Itertools;
@@ -13,21 +14,23 @@ use smallvec::{smallvec, SmallVec};
 use stdx::never;
 use syntax::{
     ast::{self, AstNode},
-    match_ast, NodeOrToken, SyntaxNode, TextRange,
+    match_ast, NodeOrToken, SyntaxNode, TextRange, TextSize,
 };
+use text_edit::TextEdit;
 
 use crate::{navigation_target::TryToNav, FileId};
 
-mod closing_brace;
-mod implicit_static;
-mod fn_lifetime_fn;
-mod closure_ret;
 mod adjustment;
-mod chaining;
-mod param_name;
-mod binding_mode;
 mod bind_pat;
+mod binding_mode;
+mod chaining;
+mod closing_brace;
+mod closure_ret;
+mod closure_captures;
 mod discriminant;
+mod fn_lifetime_fn;
+mod implicit_static;
+mod param_name;
 
 #[derive(Clone, Debug, PartialEq, Eq)]
 pub struct InlayHintsConfig {
@@ -40,11 +43,13 @@ pub struct InlayHintsConfig {
     pub adjustment_hints_mode: AdjustmentHintsMode,
     pub adjustment_hints_hide_outside_unsafe: bool,
     pub closure_return_type_hints: ClosureReturnTypeHints,
+    pub closure_capture_hints: bool,
     pub binding_mode_hints: bool,
     pub lifetime_elision_hints: LifetimeElisionHints,
     pub param_names_for_lifetime_elision_hints: bool,
     pub hide_named_constructor_hints: bool,
     pub hide_closure_initialization_hints: bool,
+    pub closure_style: ClosureStyle,
     pub max_length: Option<usize>,
     pub closing_brace_hints_min_lines: Option<usize>,
 }
@@ -87,38 +92,61 @@ pub enum AdjustmentHintsMode {
 
 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
 pub enum InlayKind {
+    Adjustment,
     BindingMode,
     Chaining,
     ClosingBrace,
-    ClosureReturnType,
+    ClosureCapture,
+    Discriminant,
     GenericParamList,
-    Adjustment,
-    AdjustmentPostfix,
     Lifetime,
     Parameter,
     Type,
-    Discriminant,
-    OpeningParenthesis,
-    ClosingParenthesis,
+}
+
+#[derive(Debug)]
+pub enum InlayHintPosition {
+    Before,
+    After,
 }
 
 #[derive(Debug)]
 pub struct InlayHint {
     /// The text range this inlay hint applies to.
     pub range: TextRange,
-    /// The kind of this inlay hint. This is used to determine side and padding of the hint for
-    /// rendering purposes.
+    pub position: InlayHintPosition,
+    pub pad_left: bool,
+    pub pad_right: bool,
+    /// The kind of this inlay hint.
     pub kind: InlayKind,
     /// The actual label to show in the inlay hint.
     pub label: InlayHintLabel,
+    /// Text edit to apply when "accepting" this inlay hint.
+    pub text_edit: Option<TextEdit>,
 }
 
 impl InlayHint {
-    fn closing_paren(range: TextRange) -> InlayHint {
-        InlayHint { range, kind: InlayKind::ClosingParenthesis, label: InlayHintLabel::from(")") }
+    fn closing_paren_after(kind: InlayKind, range: TextRange) -> InlayHint {
+        InlayHint {
+            range,
+            kind,
+            label: InlayHintLabel::from(")"),
+            text_edit: None,
+            position: InlayHintPosition::After,
+            pad_left: false,
+            pad_right: false,
+        }
     }
-    fn opening_paren(range: TextRange) -> InlayHint {
-        InlayHint { range, kind: InlayKind::OpeningParenthesis, label: InlayHintLabel::from("(") }
+    fn opening_paren_before(kind: InlayKind, range: TextRange) -> InlayHint {
+        InlayHint {
+            range,
+            kind,
+            label: InlayHintLabel::from("("),
+            text_edit: None,
+            position: InlayHintPosition::Before,
+            pad_left: false,
+            pad_right: false,
+        }
     }
 }
 
@@ -283,14 +311,15 @@ impl InlayHintLabelBuilder<'_> {
 fn label_of_ty(
     famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>,
     config: &InlayHintsConfig,
-    ty: hir::Type,
+    ty: &hir::Type,
 ) -> Option<InlayHintLabel> {
     fn rec(
         sema: &Semantics<'_, RootDatabase>,
         famous_defs: &FamousDefs<'_, '_>,
         mut max_length: Option<usize>,
-        ty: hir::Type,
+        ty: &hir::Type,
         label_builder: &mut InlayHintLabelBuilder<'_>,
+        config: &InlayHintsConfig,
     ) -> Result<(), HirDisplayError> {
         let iter_item_type = hint_iterator(sema, famous_defs, &ty);
         match iter_item_type {
@@ -321,11 +350,14 @@ fn label_of_ty(
                 label_builder.write_str(LABEL_ITEM)?;
                 label_builder.end_location_link();
                 label_builder.write_str(LABEL_MIDDLE2)?;
-                rec(sema, famous_defs, max_length, ty, label_builder)?;
+                rec(sema, famous_defs, max_length, &ty, label_builder, config)?;
                 label_builder.write_str(LABEL_END)?;
                 Ok(())
             }
-            None => ty.display_truncated(sema.db, max_length).write_to(label_builder),
+            None => ty
+                .display_truncated(sema.db, max_length)
+                .with_closure_style(config.closure_style)
+                .write_to(label_builder),
         }
     }
 
@@ -335,11 +367,28 @@ fn label_of_ty(
         location: None,
         result: InlayHintLabel::default(),
     };
-    let _ = rec(sema, famous_defs, config.max_length, ty, &mut label_builder);
+    let _ = rec(sema, famous_defs, config.max_length, ty, &mut label_builder, config);
     let r = label_builder.finish();
     Some(r)
 }
 
+fn ty_to_text_edit(
+    sema: &Semantics<'_, RootDatabase>,
+    node_for_hint: &SyntaxNode,
+    ty: &hir::Type,
+    offset_to_insert: TextSize,
+    prefix: String,
+) -> Option<TextEdit> {
+    let scope = sema.scope(node_for_hint)?;
+    // FIXME: Limit the length and bail out on excess somehow?
+    let rendered = ty.display_source_code(scope.db, scope.module().into(), false).ok()?;
+
+    let mut builder = TextEdit::builder();
+    builder.insert(offset_to_insert, prefix);
+    builder.insert(offset_to_insert, rendered);
+    Some(builder.finish())
+}
+
 // Feature: Inlay Hints
 //
 // rust-analyzer shows additional information inline with the source code.
@@ -408,10 +457,10 @@ fn hints(
                     ast::Expr::MethodCallExpr(it) => {
                         param_name::hints(hints, sema, config, ast::Expr::from(it))
                     }
-                    ast::Expr::ClosureExpr(it) => closure_ret::hints(hints, famous_defs, config, file_id, it),
-                    // We could show reborrows for all expressions, but usually that is just noise to the user
-                    // and the main point here is to show why "moving" a mutable reference doesn't necessarily move it
-                    // ast::Expr::PathExpr(_) => reborrow_hints(hints, sema, config, &expr),
+                    ast::Expr::ClosureExpr(it) => {
+                        closure_captures::hints(hints, famous_defs, config, file_id, it.clone());
+                        closure_ret::hints(hints, famous_defs, config, file_id, it)
+                    },
                     _ => None,
                 }
             },
@@ -481,6 +530,7 @@ fn closure_has_block_body(closure: &ast::ClosureExpr) -> bool {
 #[cfg(test)]
 mod tests {
     use expect_test::Expect;
+    use hir::ClosureStyle;
     use itertools::Itertools;
     use test_utils::extract_annotations;
 
@@ -498,12 +548,14 @@ mod tests {
         chaining_hints: false,
         lifetime_elision_hints: LifetimeElisionHints::Never,
         closure_return_type_hints: ClosureReturnTypeHints::Never,
+        closure_capture_hints: false,
         adjustment_hints: AdjustmentHints::Never,
         adjustment_hints_mode: AdjustmentHintsMode::Prefix,
         adjustment_hints_hide_outside_unsafe: false,
         binding_mode_hints: false,
         hide_named_constructor_hints: false,
         hide_closure_initialization_hints: false,
+        closure_style: ClosureStyle::ImplFn,
         param_names_for_lifetime_elision_hints: false,
         max_length: None,
         closing_brace_hints_min_lines: None,
@@ -530,7 +582,8 @@ mod tests {
         let inlay_hints = analysis.inlay_hints(&config, file_id, None).unwrap();
         let actual = inlay_hints
             .into_iter()
-            .map(|it| (it.range, it.label.to_string()))
+            // FIXME: We trim the start because some inlay produces leading whitespace which is not properly supported by our annotation extraction
+            .map(|it| (it.range, it.label.to_string().trim_start().to_owned()))
             .sorted_by_key(|(range, _)| range.start())
             .collect::<Vec<_>>();
         expected.sort_by_key(|(range, _)| range.start());
@@ -545,6 +598,37 @@ mod tests {
         expect.assert_debug_eq(&inlay_hints)
     }
 
+    /// Computes inlay hints for the fixture, applies all the provided text edits and then runs
+    /// expect test.
+    #[track_caller]
+    pub(super) fn check_edit(config: InlayHintsConfig, ra_fixture: &str, expect: Expect) {
+        let (analysis, file_id) = fixture::file(ra_fixture);
+        let inlay_hints = analysis.inlay_hints(&config, file_id, None).unwrap();
+
+        let edits = inlay_hints
+            .into_iter()
+            .filter_map(|hint| hint.text_edit)
+            .reduce(|mut acc, next| {
+                acc.union(next).expect("merging text edits failed");
+                acc
+            })
+            .expect("no edit returned");
+
+        let mut actual = analysis.file_text(file_id).unwrap().to_string();
+        edits.apply(&mut actual);
+        expect.assert_eq(&actual);
+    }
+
+    #[track_caller]
+    pub(super) fn check_no_edit(config: InlayHintsConfig, ra_fixture: &str) {
+        let (analysis, file_id) = fixture::file(ra_fixture);
+        let inlay_hints = analysis.inlay_hints(&config, file_id, None).unwrap();
+
+        let edits: Vec<_> = inlay_hints.into_iter().filter_map(|hint| hint.text_edit).collect();
+
+        assert!(edits.is_empty(), "unexpected edits: {edits:?}");
+    }
+
     #[test]
     fn hints_disabled() {
         check_with_config(
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs
index 46505b30441..10bee2a6acc 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs
@@ -3,7 +3,11 @@
 //! let _: u32  = /* <never-to-any> */ loop {};
 //! let _: &u32 = /* &* */ &mut 0;
 //! ```
-use hir::{Adjust, Adjustment, AutoBorrow, HirDisplay, Mutability, PointerCast, Safety, Semantics};
+use either::Either;
+use hir::{
+    Adjust, Adjustment, AutoBorrow, HirDisplay, Mutability, OverloadedDeref, PointerCast, Safety,
+    Semantics,
+};
 use ide_db::RootDatabase;
 
 use stdx::never;
@@ -13,8 +17,8 @@ use syntax::{
 };
 
 use crate::{
-    AdjustmentHints, AdjustmentHintsMode, InlayHint, InlayHintLabel, InlayHintsConfig, InlayKind,
-    InlayTooltip,
+    AdjustmentHints, AdjustmentHintsMode, InlayHint, InlayHintLabel, InlayHintPosition,
+    InlayHintsConfig, InlayKind, InlayTooltip,
 };
 
 pub(super) fn hints(
@@ -60,22 +64,26 @@ pub(super) fn hints(
         mode_and_needs_parens_for_adjustment_hints(expr, config.adjustment_hints_mode);
 
     if needs_outer_parens {
-        acc.push(InlayHint::opening_paren(expr.syntax().text_range()));
+        acc.push(InlayHint::opening_paren_before(
+            InlayKind::Adjustment,
+            expr.syntax().text_range(),
+        ));
     }
 
     if postfix && needs_inner_parens {
-        acc.push(InlayHint::opening_paren(expr.syntax().text_range()));
-        acc.push(InlayHint::closing_paren(expr.syntax().text_range()));
+        acc.push(InlayHint::opening_paren_before(
+            InlayKind::Adjustment,
+            expr.syntax().text_range(),
+        ));
+        acc.push(InlayHint::closing_paren_after(InlayKind::Adjustment, expr.syntax().text_range()));
     }
 
-    let (mut tmp0, mut tmp1);
-    let iter: &mut dyn Iterator<Item = _> = if postfix {
-        tmp0 = adjustments.into_iter();
-        &mut tmp0
+    let mut iter = if postfix {
+        Either::Left(adjustments.into_iter())
     } else {
-        tmp1 = adjustments.into_iter().rev();
-        &mut tmp1
+        Either::Right(adjustments.into_iter().rev())
     };
+    let iter: &mut dyn Iterator<Item = _> = iter.as_mut().either(|it| it as _, |it| it as _);
 
     for Adjustment { source, target, kind } in iter {
         if source == target {
@@ -88,7 +96,13 @@ pub(super) fn hints(
             Adjust::NeverToAny if config.adjustment_hints == AdjustmentHints::Always => {
                 ("<never-to-any>", "never to any")
             }
-            Adjust::Deref(_) => ("*", "dereference"),
+            Adjust::Deref(None) => ("*", "dereference"),
+            Adjust::Deref(Some(OverloadedDeref(Mutability::Shared))) => {
+                ("*", "`Deref` dereference")
+            }
+            Adjust::Deref(Some(OverloadedDeref(Mutability::Mut))) => {
+                ("*", "`DerefMut` dereference")
+            }
             Adjust::Borrow(AutoBorrow::Ref(Mutability::Shared)) => ("&", "borrow"),
             Adjust::Borrow(AutoBorrow::Ref(Mutability::Mut)) => ("&mut ", "unique borrow"),
             Adjust::Borrow(AutoBorrow::RawPtr(Mutability::Shared)) => {
@@ -125,7 +139,10 @@ pub(super) fn hints(
         };
         acc.push(InlayHint {
             range: expr.syntax().text_range(),
-            kind: if postfix { InlayKind::AdjustmentPostfix } else { InlayKind::Adjustment },
+            pad_left: false,
+            pad_right: false,
+            position: if postfix { InlayHintPosition::After } else { InlayHintPosition::Before },
+            kind: InlayKind::Adjustment,
             label: InlayHintLabel::simple(
                 if postfix { format!(".{}", text.trim_end()) } else { text.to_owned() },
                 Some(InlayTooltip::Markdown(format!(
@@ -135,19 +152,23 @@ pub(super) fn hints(
                 ))),
                 None,
             ),
+            text_edit: None,
         });
     }
     if !postfix && needs_inner_parens {
-        acc.push(InlayHint::opening_paren(expr.syntax().text_range()));
-        acc.push(InlayHint::closing_paren(expr.syntax().text_range()));
+        acc.push(InlayHint::opening_paren_before(
+            InlayKind::Adjustment,
+            expr.syntax().text_range(),
+        ));
+        acc.push(InlayHint::closing_paren_after(InlayKind::Adjustment, expr.syntax().text_range()));
     }
     if needs_outer_parens {
-        acc.push(InlayHint::closing_paren(expr.syntax().text_range()));
+        acc.push(InlayHint::closing_paren_after(InlayKind::Adjustment, expr.syntax().text_range()));
     }
     Some(())
 }
 
-/// Returns whatever the hint should be postfix and if we need to add paretheses on the inside and/or outside of `expr`,
+/// Returns whatever the hint should be postfix and if we need to add parentheses on the inside and/or outside of `expr`,
 /// if we are going to add (`postfix`) adjustments hints to it.
 fn mode_and_needs_parens_for_adjustment_hints(
     expr: &ast::Expr,
@@ -182,7 +203,7 @@ fn mode_and_needs_parens_for_adjustment_hints(
     }
 }
 
-/// Returns whatever we need to add paretheses on the inside and/or outside of `expr`,
+/// Returns whatever we need to add parentheses on the inside and/or outside of `expr`,
 /// if we are going to add (`postfix`) adjustments hints to it.
 fn needs_parens_for_adjustment_hints(expr: &ast::Expr, postfix: bool) -> (bool, bool) {
     // This is a very miserable pile of hacks...
@@ -193,10 +214,10 @@ fn needs_parens_for_adjustment_hints(expr: &ast::Expr, postfix: bool) -> (bool,
     // But we want to check what would happen if we add `*`/`.*` to the inner expression.
     // To check for inner we need `` expr.needs_parens_in(`*expr`) ``,
     // to check for outer we need `` `*expr`.needs_parens_in(parent) ``,
-    // where "expr" is the `expr` parameter, `*expr` is the editted `expr`,
+    // where "expr" is the `expr` parameter, `*expr` is the edited `expr`,
     // and "parent" is the parent of the original expression...
     //
-    // For this we utilize mutable mutable trees, which is a HACK, but it works.
+    // For this we utilize mutable trees, which is a HACK, but it works.
     //
     // FIXME: comeup with a better API for `needs_parens_in`, so that we don't have to do *this*
 
@@ -242,7 +263,7 @@ fn needs_parens_for_adjustment_hints(expr: &ast::Expr, postfix: bool) -> (bool,
     };
 
     // At this point
-    // - `parent`     is the parrent of the original expression
+    // - `parent`     is the parent of the original expression
     // - `dummy_expr` is the original expression wrapped in the operator we want (`*`/`.*`)
     // - `expr`       is the clone of the original expression (with `dummy_expr` as the parent)
 
@@ -264,7 +285,7 @@ mod tests {
         check_with_config(
             InlayHintsConfig { adjustment_hints: AdjustmentHints::Always, ..DISABLED_CONFIG },
             r#"
-//- minicore: coerce_unsized, fn, eq
+//- minicore: coerce_unsized, fn, eq, index
 fn main() {
     let _: u32         = loop {};
                        //^^^^^^^<never-to-any>
@@ -315,6 +336,8 @@ fn main() {
     (&Struct).consume();
    //^^^^^^^*
     (&Struct).by_ref();
+   //^^^^^^^&
+   //^^^^^^^*
 
     (&mut Struct).consume();
    //^^^^^^^^^^^*
@@ -322,6 +345,8 @@ fn main() {
    //^^^^^^^^^^^&
    //^^^^^^^^^^^*
     (&mut Struct).by_ref_mut();
+   //^^^^^^^^^^^&mut $
+   //^^^^^^^^^^^*
 
     // Check that block-like expressions don't duplicate hints
     let _: &mut [u32] = (&mut []);
@@ -360,6 +385,19 @@ fn main() {
     (()) == {()};
   // ^^&
          // ^^^^&
+    let closure: dyn Fn = || ();
+    closure();
+  //^^^^^^^(
+  //^^^^^^^&
+  //^^^^^^^)
+    Struct[0];
+  //^^^^^^(
+  //^^^^^^&
+  //^^^^^^)
+    &mut Struct[0];
+       //^^^^^^(
+       //^^^^^^&mut $
+       //^^^^^^)
 }
 
 #[derive(Copy, Clone)]
@@ -369,8 +407,13 @@ impl Struct {
     fn by_ref(&self) {}
     fn by_ref_mut(&mut self) {}
 }
+struct StructMut;
+impl core::ops::Index<usize> for Struct {
+    type Output = ();
+}
+impl core::ops::IndexMut for Struct {}
 "#,
-        )
+        );
     }
 
     #[test]
@@ -382,7 +425,7 @@ impl Struct {
                 ..DISABLED_CONFIG
             },
             r#"
-//- minicore: coerce_unsized, fn, eq
+//- minicore: coerce_unsized, fn, eq, index
 fn main() {
 
     Struct.consume();
@@ -396,6 +439,10 @@ fn main() {
    //^^^^^^^)
    //^^^^^^^.*
     (&Struct).by_ref();
+   //^^^^^^^(
+   //^^^^^^^)
+   //^^^^^^^.*
+   //^^^^^^^.&
 
     (&mut Struct).consume();
    //^^^^^^^^^^^(
@@ -407,6 +454,10 @@ fn main() {
    //^^^^^^^^^^^.*
    //^^^^^^^^^^^.&
     (&mut Struct).by_ref_mut();
+   //^^^^^^^^^^^(
+   //^^^^^^^^^^^)
+   //^^^^^^^^^^^.*
+   //^^^^^^^^^^^.&mut
 
     // Check that block-like expressions don't duplicate hints
     let _: &mut [u32] = (&mut []);
@@ -457,6 +508,13 @@ fn main() {
     (()) == {()};
   // ^^.&
          // ^^^^.&
+    let closure: dyn Fn = || ();
+    closure();
+  //^^^^^^^.&
+    Struct[0];
+  //^^^^^^.&
+    &mut Struct[0];
+       //^^^^^^.&mut
 }
 
 #[derive(Copy, Clone)]
@@ -466,6 +524,11 @@ impl Struct {
     fn by_ref(&self) {}
     fn by_ref_mut(&mut self) {}
 }
+struct StructMut;
+impl core::ops::Index<usize> for Struct {
+    type Output = ();
+}
+impl core::ops::IndexMut for Struct {}
 "#,
         );
     }
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs
index 6a50927333d..07b9f9cc1ff 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs
@@ -3,7 +3,7 @@
 //! fn f(a: i32, b: i32) -> i32 { a + b }
 //! let _x /* i32 */= f(4, 4);
 //! ```
-use hir::{Semantics, TypeInfo};
+use hir::Semantics;
 use ide_db::{base_db::FileId, famous_defs::FamousDefs, RootDatabase};
 
 use itertools::Itertools;
@@ -12,9 +12,10 @@ use syntax::{
     match_ast,
 };
 
-use crate::{inlay_hints::closure_has_block_body, InlayHint, InlayHintsConfig, InlayKind};
-
-use super::label_of_ty;
+use crate::{
+    inlay_hints::{closure_has_block_body, label_of_ty, ty_to_text_edit},
+    InlayHint, InlayHintPosition, InlayHintsConfig, InlayKind,
+};
 
 pub(super) fn hints(
     acc: &mut Vec<InlayHint>,
@@ -27,15 +28,45 @@ pub(super) fn hints(
         return None;
     }
 
+    let parent = pat.syntax().parent()?;
+    let type_ascriptable = match_ast! {
+        match parent {
+            ast::Param(it) => {
+                if it.ty().is_some() {
+                    return None;
+                }
+                Some(it.colon_token())
+            },
+            ast::LetStmt(it) => {
+                if config.hide_closure_initialization_hints {
+                    if let Some(ast::Expr::ClosureExpr(closure)) = it.initializer() {
+                        if closure_has_block_body(&closure) {
+                            return None;
+                        }
+                    }
+                }
+                if it.ty().is_some() {
+                    return None;
+                }
+                Some(it.colon_token())
+            },
+            _ => None
+        }
+    };
+
     let descended = sema.descend_node_into_attributes(pat.clone()).pop();
     let desc_pat = descended.as_ref().unwrap_or(pat);
-    let ty = sema.type_of_pat(&desc_pat.clone().into())?.original;
+    let ty = sema.type_of_binding_in_pat(desc_pat)?;
 
-    if should_not_display_type_hint(sema, config, pat, &ty) {
+    if ty.is_unknown() {
         return None;
     }
 
-    let label = label_of_ty(famous_defs, config, ty)?;
+    if sema.resolve_bind_pat_to_const(pat).is_some() {
+        return None;
+    }
+
+    let mut label = label_of_ty(famous_defs, config, &ty)?;
 
     if config.hide_named_constructor_hints
         && is_named_constructor(sema, pat, &label.to_string()).is_some()
@@ -43,69 +74,46 @@ pub(super) fn hints(
         return None;
     }
 
+    let text_edit = if let Some(colon_token) = &type_ascriptable {
+        ty_to_text_edit(
+            sema,
+            desc_pat.syntax(),
+            &ty,
+            colon_token
+                .as_ref()
+                .map_or_else(|| pat.syntax().text_range(), |t| t.text_range())
+                .end(),
+            if colon_token.is_some() { String::new() } else { String::from(": ") },
+        )
+    } else {
+        None
+    };
+
+    let render_colons = config.render_colons && !matches!(type_ascriptable, Some(Some(_)));
+    if render_colons {
+        label.prepend_str(": ");
+    }
+
+    let text_range = match pat.name() {
+        Some(name) => name.syntax().text_range(),
+        None => pat.syntax().text_range(),
+    };
     acc.push(InlayHint {
-        range: match pat.name() {
-            Some(name) => name.syntax().text_range(),
-            None => pat.syntax().text_range(),
+        range: match type_ascriptable {
+            Some(Some(t)) => text_range.cover(t.text_range()),
+            _ => text_range,
         },
         kind: InlayKind::Type,
         label,
+        text_edit,
+        position: InlayHintPosition::After,
+        pad_left: !render_colons,
+        pad_right: false,
     });
 
     Some(())
 }
 
-fn should_not_display_type_hint(
-    sema: &Semantics<'_, RootDatabase>,
-    config: &InlayHintsConfig,
-    bind_pat: &ast::IdentPat,
-    pat_ty: &hir::Type,
-) -> bool {
-    let db = sema.db;
-
-    if pat_ty.is_unknown() {
-        return true;
-    }
-
-    if sema.resolve_bind_pat_to_const(bind_pat).is_some() {
-        return true;
-    }
-
-    for node in bind_pat.syntax().ancestors() {
-        match_ast! {
-            match node {
-                ast::LetStmt(it) => {
-                    if config.hide_closure_initialization_hints {
-                        if let Some(ast::Expr::ClosureExpr(closure)) = it.initializer() {
-                            if closure_has_block_body(&closure) {
-                                return true;
-                            }
-                        }
-                    }
-                    return it.ty().is_some()
-                },
-                // FIXME: We might wanna show type hints in parameters for non-top level patterns as well
-                ast::Param(it) => return it.ty().is_some(),
-                ast::MatchArm(_) => return pat_is_enum_variant(db, bind_pat, pat_ty),
-                ast::LetExpr(_) => return pat_is_enum_variant(db, bind_pat, pat_ty),
-                ast::IfExpr(_) => return false,
-                ast::WhileExpr(_) => return false,
-                ast::ForExpr(it) => {
-                    // We *should* display hint only if user provided "in {expr}" and we know the type of expr (and it's not unit).
-                    // Type of expr should be iterable.
-                    return it.in_token().is_none() ||
-                        it.iterable()
-                            .and_then(|iterable_expr| sema.type_of_expr(&iterable_expr))
-                            .map(TypeInfo::original)
-                            .map_or(true, |iterable_ty| iterable_ty.is_unknown() || iterable_ty.is_unit())
-                },
-                _ => (),
-            }
-        }
-    }
-    false
-}
-
 fn is_named_constructor(
     sema: &Semantics<'_, RootDatabase>,
     pat: &ast::IdentPat,
@@ -159,30 +167,20 @@ fn is_named_constructor(
     (ctor_name == ty_name).then_some(())
 }
 
-fn pat_is_enum_variant(db: &RootDatabase, bind_pat: &ast::IdentPat, pat_ty: &hir::Type) -> bool {
-    if let Some(hir::Adt::Enum(enum_data)) = pat_ty.as_adt() {
-        let pat_text = bind_pat.to_string();
-        enum_data
-            .variants(db)
-            .into_iter()
-            .map(|variant| variant.name(db).to_smol_str())
-            .any(|enum_name| enum_name == pat_text)
-    } else {
-        false
-    }
-}
-
 #[cfg(test)]
 mod tests {
     // This module also contains tests for super::closure_ret
 
+    use expect_test::expect;
+    use hir::ClosureStyle;
     use syntax::{TextRange, TextSize};
     use test_utils::extract_annotations;
 
-    use crate::{fixture, inlay_hints::InlayHintsConfig};
+    use crate::{fixture, inlay_hints::InlayHintsConfig, ClosureReturnTypeHints};
 
-    use crate::inlay_hints::tests::{check, check_with_config, DISABLED_CONFIG, TEST_CONFIG};
-    use crate::ClosureReturnTypeHints;
+    use crate::inlay_hints::tests::{
+        check, check_edit, check_no_edit, check_with_config, DISABLED_CONFIG, TEST_CONFIG,
+    };
 
     #[track_caller]
     fn check_types(ra_fixture: &str) {
@@ -235,7 +233,7 @@ fn main() {
     let zz_ref = &zz;
       //^^^^^^ &Test<i32>
     let test = || zz;
-      //^^^^ || -> Test<i32>
+      //^^^^ impl FnOnce() -> Test<i32>
 }"#,
         );
     }
@@ -528,24 +526,7 @@ fn main() {
 struct Test { a: Option<u32>, b: u8 }
 
 fn main() {
-    let test = Some(Test { a: Some(3), b: 1 });
-      //^^^^ Option<Test>
-    if let None = &test {};
-    if let test = &test {};
-         //^^^^ &Option<Test>
-    if let Some(test) = &test {};
-              //^^^^ &Test
-    if let Some(Test { a,             b }) = &test {};
-                     //^ &Option<u32> ^ &u8
-    if let Some(Test { a: x,             b: y }) = &test {};
-                        //^ &Option<u32>    ^ &u8
-    if let Some(Test { a: Some(x),  b: y }) = &test {};
-                             //^ &u32  ^ &u8
-    if let Some(Test { a: None,  b: y }) = &test {};
-                                  //^ &u8
-    if let Some(Test { b: y, .. }) = &test {};
-                        //^ &u8
-    if test == None {}
+
 }"#,
         );
     }
@@ -560,8 +541,8 @@ struct Test { a: Option<u32>, b: u8 }
 fn main() {
     let test = Some(Test { a: Some(3), b: 1 });
       //^^^^ Option<Test>
-    while let Some(Test { a: Some(x),  b: y }) = &test {};
-                                //^ &u32  ^ &u8
+    while let Some(Test { a: Some(x),    b: y }) = &test {};
+                                //^ &u32    ^ &u8
 }"#,
         );
     }
@@ -753,7 +734,7 @@ fn main() {
     let func = times2;
     //  ^^^^ fn times2(i32) -> i32
     let closure = |x: i32| x * 2;
-    //  ^^^^^^^ |i32| -> i32
+    //  ^^^^^^^ impl Fn(i32) -> i32
 }
 
 fn fallible() -> ControlFlow<()> {
@@ -811,49 +792,90 @@ fn fallible() -> ControlFlow<()> {
     }
 
     #[test]
-    fn closures() {
-        check(
+    fn closure_style() {
+        check_with_config(
+            InlayHintsConfig { type_hints: true, ..DISABLED_CONFIG },
             r#"
+//- minicore: fn
 fn main() {
-    let mut start = 0;
-          //^^^^^ i32
-    (0..2).for_each(|increment      | { start += increment; });
-                   //^^^^^^^^^ i32
-
-    let multiply =
-      //^^^^^^^^ |i32, i32| -> i32
-      | a,     b| a * b
-      //^ i32  ^ i32
-
-    ;
-
-    let _: i32 = multiply(1,  2);
-                        //^ a ^ b
-    let multiply_ref = &multiply;
-      //^^^^^^^^^^^^ &|i32, i32| -> i32
-
-    let return_42 = || 42;
-      //^^^^^^^^^ || -> i32
-      || { 42 };
-    //^^ i32
-}"#,
+    let x = || 2;
+      //^ impl Fn() -> i32
+    let y = |t: i32| x() + t;
+      //^ impl Fn(i32) -> i32
+    let mut t = 5;
+          //^ i32
+    let z = |k: i32| { t += k; };
+      //^ impl FnMut(i32)
+    let p = (y, z);
+      //^ (impl Fn(i32) -> i32, impl FnMut(i32))
+}
+            "#,
         );
-    }
-
-    #[test]
-    fn return_type_hints_for_closure_without_block() {
         check_with_config(
             InlayHintsConfig {
-                closure_return_type_hints: ClosureReturnTypeHints::Always,
+                type_hints: true,
+                closure_style: ClosureStyle::RANotation,
                 ..DISABLED_CONFIG
             },
             r#"
+//- minicore: fn
 fn main() {
-    let a = || { 0 };
-          //^^ i32
-    let b = || 0;
-          //^^ i32
-}"#,
+    let x = || 2;
+      //^ || -> i32
+    let y = |t: i32| x() + t;
+      //^ |i32| -> i32
+    let mut t = 5;
+          //^ i32
+    let z = |k: i32| { t += k; };
+      //^ |i32| -> ()
+    let p = (y, z);
+      //^ (|i32| -> i32, |i32| -> ())
+}
+            "#,
+        );
+        check_with_config(
+            InlayHintsConfig {
+                type_hints: true,
+                closure_style: ClosureStyle::ClosureWithId,
+                ..DISABLED_CONFIG
+            },
+            r#"
+//- minicore: fn
+fn main() {
+    let x = || 2;
+      //^ {closure#0}
+    let y = |t: i32| x() + t;
+      //^ {closure#1}
+    let mut t = 5;
+          //^ i32
+    let z = |k: i32| { t += k; };
+      //^ {closure#2}
+    let p = (y, z);
+      //^ ({closure#1}, {closure#2})
+}
+            "#,
+        );
+        check_with_config(
+            InlayHintsConfig {
+                type_hints: true,
+                closure_style: ClosureStyle::Hide,
+                ..DISABLED_CONFIG
+            },
+            r#"
+//- minicore: fn
+fn main() {
+    let x = || 2;
+      //^ …
+    let y = |t: i32| x() + t;
+      //^ …
+    let mut t = 5;
+          //^ i32
+    let z = |k: i32| { t += k; };
+      //^ …
+    let p = (y, z);
+      //^ (…, …)
+}
+            "#,
         );
     }
 
@@ -871,13 +893,13 @@ fn main() {
     let multiple_2 = |x: i32| { x * 2 };
 
     let multiple_2 = |x: i32| x * 2;
-    //  ^^^^^^^^^^ |i32| -> i32
+    //  ^^^^^^^^^^ impl Fn(i32) -> i32
 
     let (not) = (|x: bool| { !x });
-    //   ^^^ |bool| -> bool
+    //   ^^^ impl Fn(bool) -> bool
 
     let (is_zero, _b) = (|x: usize| { x == 0 }, false);
-    //   ^^^^^^^ |usize| -> bool
+    //   ^^^^^^^ impl Fn(usize) -> bool
     //            ^^ bool
 
     let plus_one = |x| { x + 1 };
@@ -923,4 +945,160 @@ fn main() {
 }"#,
         );
     }
+
+    #[test]
+    fn edit_for_let_stmt() {
+        check_edit(
+            TEST_CONFIG,
+            r#"
+struct S<T>(T);
+fn test<F>(v: S<(S<i32>, S<()>)>, f: F) {
+    let a = v;
+    let S((b, c)) = v;
+    let a @ S((b, c)) = v;
+    let a = f;
+}
+"#,
+            expect![[r#"
+                struct S<T>(T);
+                fn test<F>(v: S<(S<i32>, S<()>)>, f: F) {
+                    let a: S<(S<i32>, S<()>)> = v;
+                    let S((b, c)) = v;
+                    let a @ S((b, c)): S<(S<i32>, S<()>)> = v;
+                    let a: F = f;
+                }
+            "#]],
+        );
+    }
+
+    #[test]
+    fn edit_for_closure_param() {
+        check_edit(
+            TEST_CONFIG,
+            r#"
+fn test<T>(t: T) {
+    let f = |a, b, c| {};
+    let result = f(42, "", t);
+}
+"#,
+            expect![[r#"
+                fn test<T>(t: T) {
+                    let f = |a: i32, b: &str, c: T| {};
+                    let result: () = f(42, "", t);
+                }
+            "#]],
+        );
+    }
+
+    #[test]
+    fn edit_for_closure_ret() {
+        check_edit(
+            TEST_CONFIG,
+            r#"
+struct S<T>(T);
+fn test() {
+    let f = || { 3 };
+    let f = |a: S<usize>| { S(a) };
+}
+"#,
+            expect![[r#"
+                struct S<T>(T);
+                fn test() {
+                    let f = || -> i32 { 3 };
+                    let f = |a: S<usize>| -> S<S<usize>> { S(a) };
+                }
+            "#]],
+        );
+    }
+
+    #[test]
+    fn edit_prefixes_paths() {
+        check_edit(
+            TEST_CONFIG,
+            r#"
+pub struct S<T>(T);
+mod middle {
+    pub struct S<T, U>(T, U);
+    pub fn make() -> S<inner::S<i64>, super::S<usize>> { loop {} }
+
+    mod inner {
+        pub struct S<T>(T);
+    }
+
+    fn test() {
+        let a = make();
+    }
+}
+"#,
+            expect![[r#"
+                pub struct S<T>(T);
+                mod middle {
+                    pub struct S<T, U>(T, U);
+                    pub fn make() -> S<inner::S<i64>, super::S<usize>> { loop {} }
+
+                    mod inner {
+                        pub struct S<T>(T);
+                    }
+
+                    fn test() {
+                        let a: S<inner::S<i64>, crate::S<usize>> = make();
+                    }
+                }
+            "#]],
+        );
+    }
+
+    #[test]
+    fn no_edit_for_top_pat_where_type_annotation_is_invalid() {
+        check_no_edit(
+            TEST_CONFIG,
+            r#"
+fn test() {
+    if let a = 42 {}
+    while let a = 42 {}
+    match 42 {
+        a => (),
+    }
+}
+"#,
+        )
+    }
+
+    #[test]
+    fn no_edit_for_opaque_type() {
+        check_no_edit(
+            TEST_CONFIG,
+            r#"
+trait Trait {}
+struct S<T>(T);
+fn foo() -> impl Trait {}
+fn bar() -> S<impl Trait> {}
+fn test() {
+    let a = foo();
+    let a = bar();
+    let f = || { foo() };
+    let f = || { bar() };
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn no_edit_for_closure_return_without_body_block() {
+        // We can lift this limitation; see FIXME in closure_ret module.
+        let config = InlayHintsConfig {
+            closure_return_type_hints: ClosureReturnTypeHints::Always,
+            ..TEST_CONFIG
+        };
+        check_no_edit(
+            config,
+            r#"
+struct S<T>(T);
+fn test() {
+    let f = || 3;
+    let f = |a: S<usize>| S(a);
+}
+"#,
+        );
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs
index 5d9729263c2..343cf17e50e 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs
@@ -7,7 +7,7 @@ use ide_db::RootDatabase;
 
 use syntax::ast::{self, AstNode};
 
-use crate::{InlayHint, InlayHintsConfig, InlayKind};
+use crate::{InlayHint, InlayHintPosition, InlayHintsConfig, InlayKind};
 
 pub(super) fn hints(
     acc: &mut Vec<InlayHint>,
@@ -49,7 +49,15 @@ pub(super) fn hints(
             (true, false) => "&",
             _ => return,
         };
-        acc.push(InlayHint { range, kind: InlayKind::BindingMode, label: r.to_string().into() });
+        acc.push(InlayHint {
+            range,
+            kind: InlayKind::BindingMode,
+            label: r.to_string().into(),
+            text_edit: None,
+            position: InlayHintPosition::Before,
+            pad_left: false,
+            pad_right: mut_reference,
+        });
     });
     match pat {
         ast::Pat::IdentPat(pat) if pat.ref_token().is_none() && pat.mut_token().is_none() => {
@@ -63,11 +71,21 @@ pub(super) fn hints(
                 range: pat.syntax().text_range(),
                 kind: InlayKind::BindingMode,
                 label: bm.to_string().into(),
+                text_edit: None,
+                position: InlayHintPosition::Before,
+                pad_left: false,
+                pad_right: true,
             });
         }
         ast::Pat::OrPat(pat) if !pattern_adjustments.is_empty() && outer_paren_pat.is_none() => {
-            acc.push(InlayHint::opening_paren(pat.syntax().text_range()));
-            acc.push(InlayHint::closing_paren(pat.syntax().text_range()));
+            acc.push(InlayHint::opening_paren_before(
+                InlayKind::BindingMode,
+                pat.syntax().text_range(),
+            ));
+            acc.push(InlayHint::closing_paren_after(
+                InlayKind::BindingMode,
+                pat.syntax().text_range(),
+            ));
         }
         _ => (),
     }
@@ -142,7 +160,6 @@ struct Struct {
     field: &'static str,
 }
 fn foo(s @ Struct { field, .. }: &Struct) {}
-     //^^^^^^^^^^^^^^^^^^^^^^^^ref
          //^^^^^^^^^^^^^^^^^^^^&
                   //^^^^^ref
 "#,
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs
index 1e1771259b1..ce1e03a069b 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs
@@ -5,7 +5,7 @@ use syntax::{
     Direction, NodeOrToken, SyntaxKind, T,
 };
 
-use crate::{FileId, InlayHint, InlayHintsConfig, InlayKind};
+use crate::{FileId, InlayHint, InlayHintPosition, InlayHintsConfig, InlayKind};
 
 use super::label_of_ty;
 
@@ -60,7 +60,11 @@ pub(super) fn hints(
             acc.push(InlayHint {
                 range: expr.syntax().text_range(),
                 kind: InlayKind::Chaining,
-                label: label_of_ty(famous_defs, config, ty)?,
+                label: label_of_ty(famous_defs, config, &ty)?,
+                text_edit: None,
+                position: InlayHintPosition::After,
+                pad_left: true,
+                pad_right: false,
             });
         }
     }
@@ -103,6 +107,9 @@ fn main() {
                 [
                     InlayHint {
                         range: 147..172,
+                        position: After,
+                        pad_left: true,
+                        pad_right: false,
                         kind: Chaining,
                         label: [
                             "",
@@ -120,9 +127,13 @@ fn main() {
                             },
                             "",
                         ],
+                        text_edit: None,
                     },
                     InlayHint {
                         range: 147..154,
+                        position: After,
+                        pad_left: true,
+                        pad_right: false,
                         kind: Chaining,
                         label: [
                             "",
@@ -140,6 +151,7 @@ fn main() {
                             },
                             "",
                         ],
+                        text_edit: None,
                     },
                 ]
             "#]],
@@ -188,6 +200,9 @@ fn main() {
                 [
                     InlayHint {
                         range: 143..190,
+                        position: After,
+                        pad_left: true,
+                        pad_right: false,
                         kind: Chaining,
                         label: [
                             "",
@@ -205,9 +220,13 @@ fn main() {
                             },
                             "",
                         ],
+                        text_edit: None,
                     },
                     InlayHint {
                         range: 143..179,
+                        position: After,
+                        pad_left: true,
+                        pad_right: false,
                         kind: Chaining,
                         label: [
                             "",
@@ -225,6 +244,7 @@ fn main() {
                             },
                             "",
                         ],
+                        text_edit: None,
                     },
                 ]
             "#]],
@@ -257,6 +277,9 @@ fn main() {
                 [
                     InlayHint {
                         range: 143..190,
+                        position: After,
+                        pad_left: true,
+                        pad_right: false,
                         kind: Chaining,
                         label: [
                             "",
@@ -274,9 +297,13 @@ fn main() {
                             },
                             "",
                         ],
+                        text_edit: None,
                     },
                     InlayHint {
                         range: 143..179,
+                        position: After,
+                        pad_left: true,
+                        pad_right: false,
                         kind: Chaining,
                         label: [
                             "",
@@ -294,6 +321,7 @@ fn main() {
                             },
                             "",
                         ],
+                        text_edit: None,
                     },
                 ]
             "#]],
@@ -327,6 +355,9 @@ fn main() {
                 [
                     InlayHint {
                         range: 246..283,
+                        position: After,
+                        pad_left: true,
+                        pad_right: false,
                         kind: Chaining,
                         label: [
                             "",
@@ -357,9 +388,13 @@ fn main() {
                             },
                             "<i32, bool>>",
                         ],
+                        text_edit: None,
                     },
                     InlayHint {
                         range: 246..265,
+                        position: After,
+                        pad_left: true,
+                        pad_right: false,
                         kind: Chaining,
                         label: [
                             "",
@@ -390,6 +425,7 @@ fn main() {
                             },
                             "<i32, bool>>",
                         ],
+                        text_edit: None,
                     },
                 ]
             "#]],
@@ -425,6 +461,9 @@ fn main() {
                 [
                     InlayHint {
                         range: 174..241,
+                        position: After,
+                        pad_left: true,
+                        pad_right: false,
                         kind: Chaining,
                         label: [
                             "impl ",
@@ -435,7 +474,7 @@ fn main() {
                                         file_id: FileId(
                                             1,
                                         ),
-                                        range: 3415..3423,
+                                        range: 9332..9340,
                                     },
                                 ),
                                 tooltip: "",
@@ -448,16 +487,20 @@ fn main() {
                                         file_id: FileId(
                                             1,
                                         ),
-                                        range: 3447..3451,
+                                        range: 9364..9368,
                                     },
                                 ),
                                 tooltip: "",
                             },
                             " = ()>",
                         ],
+                        text_edit: None,
                     },
                     InlayHint {
                         range: 174..224,
+                        position: After,
+                        pad_left: true,
+                        pad_right: false,
                         kind: Chaining,
                         label: [
                             "impl ",
@@ -468,7 +511,7 @@ fn main() {
                                         file_id: FileId(
                                             1,
                                         ),
-                                        range: 3415..3423,
+                                        range: 9332..9340,
                                     },
                                 ),
                                 tooltip: "",
@@ -481,16 +524,20 @@ fn main() {
                                         file_id: FileId(
                                             1,
                                         ),
-                                        range: 3447..3451,
+                                        range: 9364..9368,
                                     },
                                 ),
                                 tooltip: "",
                             },
                             " = ()>",
                         ],
+                        text_edit: None,
                     },
                     InlayHint {
                         range: 174..206,
+                        position: After,
+                        pad_left: true,
+                        pad_right: false,
                         kind: Chaining,
                         label: [
                             "impl ",
@@ -501,7 +548,7 @@ fn main() {
                                         file_id: FileId(
                                             1,
                                         ),
-                                        range: 3415..3423,
+                                        range: 9332..9340,
                                     },
                                 ),
                                 tooltip: "",
@@ -514,16 +561,20 @@ fn main() {
                                         file_id: FileId(
                                             1,
                                         ),
-                                        range: 3447..3451,
+                                        range: 9364..9368,
                                     },
                                 ),
                                 tooltip: "",
                             },
                             " = ()>",
                         ],
+                        text_edit: None,
                     },
                     InlayHint {
                         range: 174..189,
+                        position: After,
+                        pad_left: true,
+                        pad_right: false,
                         kind: Chaining,
                         label: [
                             "&mut ",
@@ -541,6 +592,7 @@ fn main() {
                             },
                             "",
                         ],
+                        text_edit: None,
                     },
                 ]
             "#]],
@@ -573,6 +625,9 @@ fn main() {
                 [
                     InlayHint {
                         range: 124..130,
+                        position: After,
+                        pad_left: true,
+                        pad_right: false,
                         kind: Type,
                         label: [
                             "",
@@ -590,9 +645,22 @@ fn main() {
                             },
                             "",
                         ],
+                        text_edit: Some(
+                            TextEdit {
+                                indels: [
+                                    Indel {
+                                        insert: ": Struct",
+                                        delete: 130..130,
+                                    },
+                                ],
+                            },
+                        ),
                     },
                     InlayHint {
                         range: 145..185,
+                        position: After,
+                        pad_left: true,
+                        pad_right: false,
                         kind: Chaining,
                         label: [
                             "",
@@ -610,9 +678,13 @@ fn main() {
                             },
                             "",
                         ],
+                        text_edit: None,
                     },
                     InlayHint {
                         range: 145..168,
+                        position: After,
+                        pad_left: true,
+                        pad_right: false,
                         kind: Chaining,
                         label: [
                             "",
@@ -630,9 +702,13 @@ fn main() {
                             },
                             "",
                         ],
+                        text_edit: None,
                     },
                     InlayHint {
                         range: 222..228,
+                        position: Before,
+                        pad_left: false,
+                        pad_right: true,
                         kind: Parameter,
                         label: [
                             InlayHintLabelPart {
@@ -648,6 +724,7 @@ fn main() {
                                 tooltip: "",
                             },
                         ],
+                        text_edit: None,
                     },
                 ]
             "#]],
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs
index 14c11be54ef..2cefd5acdc2 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs
@@ -10,7 +10,7 @@ use syntax::{
     match_ast, SyntaxKind, SyntaxNode, T,
 };
 
-use crate::{FileId, InlayHint, InlayHintLabel, InlayHintsConfig, InlayKind};
+use crate::{FileId, InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, InlayKind};
 
 pub(super) fn hints(
     acc: &mut Vec<InlayHint>,
@@ -35,7 +35,7 @@ pub(super) fn hints(
                     let ty = imp.self_ty(sema.db);
                     let trait_ = imp.trait_(sema.db);
                     let hint_text = match trait_ {
-                        Some(tr) => format!("impl {} for {}", tr.name(sema.db), ty.display_truncated(sema.db, config.max_length)),
+                        Some(tr) => format!("impl {} for {}", tr.name(sema.db).display(sema.db), ty.display_truncated(sema.db, config.max_length)),
                         None => format!("impl {}", ty.display_truncated(sema.db, config.max_length)),
                     };
                     (hint_text, None)
@@ -112,6 +112,10 @@ pub(super) fn hints(
         range: closing_token.text_range(),
         kind: InlayKind::ClosingBrace,
         label: InlayHintLabel::simple(label, None, linked_location),
+        text_edit: None,
+        position: InlayHintPosition::After,
+        pad_left: true,
+        pad_right: false,
     });
 
     None
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs
new file mode 100644
index 00000000000..9d5defcbb71
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs
@@ -0,0 +1,207 @@
+//! Implementation of "closure return type" inlay hints.
+//!
+//! Tests live in [`bind_pat`][super::bind_pat] module.
+use ide_db::{base_db::FileId, famous_defs::FamousDefs};
+use syntax::ast::{self, AstNode};
+use text_edit::{TextRange, TextSize};
+
+use crate::{InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, InlayKind};
+
+pub(super) fn hints(
+    acc: &mut Vec<InlayHint>,
+    FamousDefs(sema, _): &FamousDefs<'_, '_>,
+    config: &InlayHintsConfig,
+    _file_id: FileId,
+    closure: ast::ClosureExpr,
+) -> Option<()> {
+    if !config.closure_capture_hints {
+        return None;
+    }
+    let ty = &sema.type_of_expr(&closure.clone().into())?.original;
+    let c = ty.as_closure()?;
+    let captures = c.captured_items(sema.db);
+
+    if captures.is_empty() {
+        return None;
+    }
+
+    let move_kw_range = match closure.move_token() {
+        Some(t) => t.text_range(),
+        None => {
+            let range = closure.syntax().first_token()?.prev_token()?.text_range();
+            let range = TextRange::new(range.end() - TextSize::from(1), range.end());
+            acc.push(InlayHint {
+                range,
+                kind: InlayKind::ClosureCapture,
+                label: InlayHintLabel::simple("move", None, None),
+                text_edit: None,
+                position: InlayHintPosition::After,
+                pad_left: false,
+                pad_right: false,
+            });
+            range
+        }
+    };
+    acc.push(InlayHint {
+        range: move_kw_range,
+        kind: InlayKind::ClosureCapture,
+        label: InlayHintLabel::from("("),
+        text_edit: None,
+        position: InlayHintPosition::After,
+        pad_left: false,
+        pad_right: false,
+    });
+    let last = captures.len() - 1;
+    for (idx, capture) in captures.into_iter().enumerate() {
+        let local = capture.local();
+        let source = local.primary_source(sema.db);
+
+        // force cache the source file, otherwise sema lookup will potentially panic
+        _ = sema.parse_or_expand(source.file());
+
+        acc.push(InlayHint {
+            range: move_kw_range,
+            kind: InlayKind::ClosureCapture,
+            label: InlayHintLabel::simple(
+                format!(
+                    "{}{}",
+                    match capture.kind() {
+                        hir::CaptureKind::SharedRef => "&",
+                        hir::CaptureKind::UniqueSharedRef => "&unique ",
+                        hir::CaptureKind::MutableRef => "&mut ",
+                        hir::CaptureKind::Move => "",
+                    },
+                    capture.display_place(sema.db)
+                ),
+                None,
+                source.name().and_then(|name| name.syntax().original_file_range_opt(sema.db)),
+            ),
+            text_edit: None,
+            position: InlayHintPosition::After,
+            pad_left: false,
+            pad_right: false,
+        });
+
+        if idx != last {
+            acc.push(InlayHint {
+                range: move_kw_range,
+                kind: InlayKind::ClosureCapture,
+                label: InlayHintLabel::simple(", ", None, None),
+                text_edit: None,
+                position: InlayHintPosition::After,
+                pad_left: false,
+                pad_right: false,
+            });
+        }
+    }
+    acc.push(InlayHint {
+        range: move_kw_range,
+        kind: InlayKind::ClosureCapture,
+        label: InlayHintLabel::from(")"),
+        text_edit: None,
+        position: InlayHintPosition::After,
+        pad_left: false,
+        pad_right: true,
+    });
+
+    Some(())
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::{
+        inlay_hints::tests::{check_with_config, DISABLED_CONFIG},
+        InlayHintsConfig,
+    };
+
+    #[test]
+    fn all_capture_kinds() {
+        check_with_config(
+            InlayHintsConfig { closure_capture_hints: true, ..DISABLED_CONFIG },
+            r#"
+//- minicore: copy, derive
+
+
+#[derive(Copy, Clone)]
+struct Copy;
+
+struct NonCopy;
+
+fn main() {
+    let foo = Copy;
+    let bar = NonCopy;
+    let mut baz = NonCopy;
+    let qux = &mut NonCopy;
+    || {
+// ^ move
+// ^ (
+// ^ &foo
+// ^ , $
+// ^ bar
+// ^ , $
+// ^ baz
+// ^ , $
+// ^ qux
+// ^ )
+        foo;
+        bar;
+        baz;
+        qux;
+    };
+    || {
+// ^ move
+// ^ (
+// ^ &foo
+// ^ , $
+// ^ &bar
+// ^ , $
+// ^ &baz
+// ^ , $
+// ^ &qux
+// ^ )
+        &foo;
+        &bar;
+        &baz;
+        &qux;
+    };
+    || {
+// ^ move
+// ^ (
+// ^ &mut baz
+// ^ )
+        &mut baz;
+    };
+    || {
+// ^ move
+// ^ (
+// ^ &mut baz
+// ^ , $
+// ^ &mut *qux
+// ^ )
+        baz = NonCopy;
+        *qux = NonCopy;
+    };
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn move_token() {
+        check_with_config(
+            InlayHintsConfig { closure_capture_hints: true, ..DISABLED_CONFIG },
+            r#"
+//- minicore: copy, derive
+fn main() {
+    let foo = u32;
+    move || {
+//  ^^^^ (
+//  ^^^^ foo
+//  ^^^^ )
+        foo;
+    };
+}
+"#,
+        );
+    }
+}
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_ret.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_ret.rs
index f03a18b8e96..3b41db0f13d 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_ret.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_ret.rs
@@ -1,14 +1,14 @@
 //! Implementation of "closure return type" inlay hints.
+//!
+//! Tests live in [`bind_pat`][super::bind_pat] module.
 use ide_db::{base_db::FileId, famous_defs::FamousDefs};
 use syntax::ast::{self, AstNode};
 
 use crate::{
-    inlay_hints::closure_has_block_body, ClosureReturnTypeHints, InlayHint, InlayHintsConfig,
-    InlayKind,
+    inlay_hints::{closure_has_block_body, label_of_ty, ty_to_text_edit},
+    ClosureReturnTypeHints, InlayHint, InlayHintPosition, InlayHintsConfig, InlayKind,
 };
 
-use super::label_of_ty;
-
 pub(super) fn hints(
     acc: &mut Vec<InlayHint>,
     famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>,
@@ -20,29 +20,81 @@ pub(super) fn hints(
         return None;
     }
 
-    if closure.ret_type().is_some() {
-        return None;
-    }
+    let ret_type = closure.ret_type().map(|rt| (rt.thin_arrow_token(), rt.ty().is_some()));
+    let arrow = match ret_type {
+        Some((_, true)) => return None,
+        Some((arrow, _)) => arrow,
+        None => None,
+    };
 
-    if !closure_has_block_body(&closure)
-        && config.closure_return_type_hints == ClosureReturnTypeHints::WithBlock
-    {
+    let has_block_body = closure_has_block_body(&closure);
+    if !has_block_body && config.closure_return_type_hints == ClosureReturnTypeHints::WithBlock {
         return None;
     }
 
     let param_list = closure.param_list()?;
 
     let closure = sema.descend_node_into_attributes(closure).pop()?;
-    let ty = sema.type_of_expr(&ast::Expr::ClosureExpr(closure))?.adjusted();
+    let ty = sema.type_of_expr(&ast::Expr::ClosureExpr(closure.clone()))?.adjusted();
     let callable = ty.as_callable(sema.db)?;
     let ty = callable.return_type();
-    if ty.is_unit() {
+    if arrow.is_none() && ty.is_unit() {
         return None;
     }
+
+    let mut label = label_of_ty(famous_defs, config, &ty)?;
+
+    if arrow.is_none() {
+        label.prepend_str(" -> ");
+    }
+    // FIXME?: We could provide text edit to insert braces for closures with non-block body.
+    let text_edit = if has_block_body {
+        ty_to_text_edit(
+            sema,
+            closure.syntax(),
+            &ty,
+            arrow
+                .as_ref()
+                .map_or_else(|| param_list.syntax().text_range(), |t| t.text_range())
+                .end(),
+            if arrow.is_none() { String::from(" -> ") } else { String::new() },
+        )
+    } else {
+        None
+    };
+
     acc.push(InlayHint {
         range: param_list.syntax().text_range(),
-        kind: InlayKind::ClosureReturnType,
-        label: label_of_ty(famous_defs, config, ty)?,
+        kind: InlayKind::Type,
+        label,
+        text_edit,
+        position: InlayHintPosition::After,
+        pad_left: false,
+        pad_right: false,
     });
     Some(())
 }
+
+#[cfg(test)]
+mod tests {
+    use crate::inlay_hints::tests::{check_with_config, DISABLED_CONFIG};
+
+    use super::*;
+
+    #[test]
+    fn return_type_hints_for_closure_without_block() {
+        check_with_config(
+            InlayHintsConfig {
+                closure_return_type_hints: ClosureReturnTypeHints::Always,
+                ..DISABLED_CONFIG
+            },
+            r#"
+fn main() {
+    let a = || { 0 };
+          //^^ -> i32
+    let b = || 0;
+          //^^ -> i32
+}"#,
+        );
+    }
+}
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs
index 67eaa553ada..c4d2ac75cfa 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs
@@ -9,7 +9,8 @@ use ide_db::{base_db::FileId, famous_defs::FamousDefs, RootDatabase};
 use syntax::ast::{self, AstNode, HasName};
 
 use crate::{
-    DiscriminantHints, InlayHint, InlayHintLabel, InlayHintsConfig, InlayKind, InlayTooltip,
+    DiscriminantHints, InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, InlayKind,
+    InlayTooltip,
 };
 
 pub(super) fn enum_hints(
@@ -19,21 +20,23 @@ pub(super) fn enum_hints(
     _: FileId,
     enum_: ast::Enum,
 ) -> Option<()> {
-    let enabled = match config.discriminant_hints {
-        DiscriminantHints::Always => true,
-        DiscriminantHints::Fieldless => {
-            !sema.to_def(&enum_)?.is_data_carrying(sema.db)
-                || enum_.variant_list()?.variants().any(|v| v.expr().is_some())
-        }
-        DiscriminantHints::Never => false,
-    };
-    if !enabled {
+    if let DiscriminantHints::Never = config.discriminant_hints {
+        return None;
+    }
+
+    let def = sema.to_def(&enum_)?;
+    let data_carrying = def.is_data_carrying(sema.db);
+    if matches!(config.discriminant_hints, DiscriminantHints::Fieldless) && data_carrying {
+        return None;
+    }
+    // data carrying enums without a primitive repr have no stable discriminants
+    if data_carrying && def.repr(sema.db).map_or(true, |r| r.int.is_none()) {
         return None;
     }
     for variant in enum_.variant_list()?.variants() {
         variant_hints(acc, sema, &variant);
     }
-    None
+    Some(())
 }
 
 fn variant_hints(
@@ -41,10 +44,11 @@ fn variant_hints(
     sema: &Semantics<'_, RootDatabase>,
     variant: &ast::Variant,
 ) -> Option<()> {
-    if variant.eq_token().is_some() {
+    if variant.expr().is_some() {
         return None;
     }
 
+    let eq_token = variant.eq_token();
     let name = variant.name()?;
 
     let descended = sema.descend_node_into_attributes(variant.clone()).pop();
@@ -52,34 +56,43 @@ fn variant_hints(
     let v = sema.to_def(desc_pat)?;
     let d = v.eval(sema.db);
 
+    let range = match variant.field_list() {
+        Some(field_list) => name.syntax().text_range().cover(field_list.syntax().text_range()),
+        None => name.syntax().text_range(),
+    };
+    let eq_ = if eq_token.is_none() { " =" } else { "" };
+    let label = InlayHintLabel::simple(
+        match d {
+            Ok(x) => {
+                if x >= 10 {
+                    format!("{eq_} {x} ({x:#X})")
+                } else {
+                    format!("{eq_} {x}")
+                }
+            }
+            Err(_) => format!("{eq_} ?"),
+        },
+        Some(InlayTooltip::String(match &d {
+            Ok(_) => "enum variant discriminant".into(),
+            Err(e) => format!("{e:?}").into(),
+        })),
+        None,
+    );
     acc.push(InlayHint {
-        range: match variant.field_list() {
-            Some(field_list) => name.syntax().text_range().cover(field_list.syntax().text_range()),
-            None => name.syntax().text_range(),
+        range: match eq_token {
+            Some(t) => range.cover(t.text_range()),
+            _ => range,
         },
         kind: InlayKind::Discriminant,
-        label: InlayHintLabel::simple(
-            match d {
-                Ok(x) => {
-                    if x >= 10 {
-                        format!("{x} ({x:#X})")
-                    } else {
-                        format!("{x}")
-                    }
-                }
-                Err(_) => "?".into(),
-            },
-            Some(InlayTooltip::String(match &d {
-                Ok(_) => "enum variant discriminant".into(),
-                Err(e) => format!("{e:?}").into(),
-            })),
-            None,
-        ),
+        label,
+        text_edit: None,
+        position: InlayHintPosition::After,
+        pad_left: false,
+        pad_right: false,
     });
 
     Some(())
 }
-
 #[cfg(test)]
 mod tests {
     use crate::inlay_hints::{
@@ -111,30 +124,30 @@ mod tests {
         check_discriminants(
             r#"
 enum Enum {
-  Variant,
-//^^^^^^^0
-  Variant1,
-//^^^^^^^^1
-  Variant2,
-//^^^^^^^^2
-  Variant5 = 5,
-  Variant6,
-//^^^^^^^^6
+    Variant,
+//  ^^^^^^^ = 0$
+    Variant1,
+//  ^^^^^^^^ = 1$
+    Variant2,
+//  ^^^^^^^^ = 2$
+    Variant5 = 5,
+    Variant6,
+//  ^^^^^^^^ = 6$
 }
 "#,
         );
         check_discriminants_fieldless(
             r#"
 enum Enum {
-  Variant,
-//^^^^^^^0
-  Variant1,
-//^^^^^^^^1
-  Variant2,
-//^^^^^^^^2
-  Variant5 = 5,
-  Variant6,
-//^^^^^^^^6
+    Variant,
+//  ^^^^^^^ = 0
+    Variant1,
+//  ^^^^^^^^ = 1
+    Variant2,
+//  ^^^^^^^^ = 2
+    Variant5 = 5,
+    Variant6,
+//  ^^^^^^^^ = 6
 }
 "#,
         );
@@ -144,26 +157,23 @@ enum Enum {
     fn datacarrying_mixed() {
         check_discriminants(
             r#"
+#[repr(u8)]
 enum Enum {
     Variant(),
-  //^^^^^^^^^0
+//  ^^^^^^^^^ = 0
     Variant1,
-  //^^^^^^^^1
+//  ^^^^^^^^ = 1
     Variant2 {},
-  //^^^^^^^^^^^2
+//  ^^^^^^^^^^^ = 2
     Variant3,
-  //^^^^^^^^3
+//  ^^^^^^^^ = 3
     Variant5 = 5,
     Variant6,
-  //^^^^^^^^6
+//  ^^^^^^^^ = 6
 }
 "#,
         );
-    }
-
-    #[test]
-    fn datacarrying_mixed_fieldless_set() {
-        check_discriminants_fieldless(
+        check_discriminants(
             r#"
 enum Enum {
     Variant(),
@@ -175,20 +185,20 @@ enum Enum {
 }
 "#,
         );
+    }
+
+    #[test]
+    fn datacarrying_mixed_fieldless_set() {
         check_discriminants_fieldless(
             r#"
+#[repr(u8)]
 enum Enum {
     Variant(),
-  //^^^^^^^^^0
     Variant1,
-  //^^^^^^^^1
     Variant2 {},
-  //^^^^^^^^^^^2
     Variant3,
-  //^^^^^^^^3
-    Variant5 = 5,
+    Variant5,
     Variant6,
-  //^^^^^^^^6
 }
 "#,
         );
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/fn_lifetime_fn.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/fn_lifetime_fn.rs
index b7182085b31..5fce11b785a 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/fn_lifetime_fn.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/fn_lifetime_fn.rs
@@ -10,7 +10,7 @@ use syntax::{
     SyntaxToken,
 };
 
-use crate::{InlayHint, InlayHintsConfig, InlayKind, LifetimeElisionHints};
+use crate::{InlayHint, InlayHintPosition, InlayHintsConfig, InlayKind, LifetimeElisionHints};
 
 pub(super) fn hints(
     acc: &mut Vec<InlayHint>,
@@ -25,6 +25,10 @@ pub(super) fn hints(
         range: t.text_range(),
         kind: InlayKind::Lifetime,
         label: label.into(),
+        text_edit: None,
+        position: InlayHintPosition::After,
+        pad_left: false,
+        pad_right: true,
     };
 
     let param_list = func.param_list()?;
@@ -189,12 +193,20 @@ pub(super) fn hints(
                     if is_empty { "" } else { ", " }
                 )
                 .into(),
+                text_edit: None,
+                position: InlayHintPosition::After,
+                pad_left: false,
+                pad_right: true,
             });
         }
         (None, allocated_lifetimes) => acc.push(InlayHint {
             range: func.name()?.syntax().text_range(),
             kind: InlayKind::GenericParamList,
             label: format!("<{}>", allocated_lifetimes.iter().format(", "),).into(),
+            text_edit: None,
+            position: InlayHintPosition::After,
+            pad_left: false,
+            pad_right: false,
         }),
     }
     Some(())
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_static.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_static.rs
index 1122ee2e392..fc297a8d824 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_static.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_static.rs
@@ -8,7 +8,7 @@ use syntax::{
     SyntaxKind,
 };
 
-use crate::{InlayHint, InlayHintsConfig, InlayKind, LifetimeElisionHints};
+use crate::{InlayHint, InlayHintPosition, InlayHintsConfig, InlayKind, LifetimeElisionHints};
 
 pub(super) fn hints(
     acc: &mut Vec<InlayHint>,
@@ -34,6 +34,10 @@ pub(super) fn hints(
                 range: t.text_range(),
                 kind: InlayKind::Lifetime,
                 label: "'static".to_owned().into(),
+                text_edit: None,
+                position: InlayHintPosition::After,
+                pad_left: false,
+                pad_right: true,
             });
         }
     }
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs
index 9cdae632410..c4f43f41175 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs
@@ -10,7 +10,7 @@ use ide_db::{base_db::FileRange, RootDatabase};
 use stdx::to_lower_snake_case;
 use syntax::ast::{self, AstNode, HasArgList, HasName, UnaryOp};
 
-use crate::{InlayHint, InlayHintLabel, InlayHintsConfig, InlayKind};
+use crate::{InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, InlayKind};
 
 pub(super) fn hints(
     acc: &mut Vec<InlayHint>,
@@ -31,16 +31,16 @@ pub(super) fn hints(
             // Only annotate hints for expressions that exist in the original file
             let range = sema.original_range_opt(arg.syntax())?;
             let (param_name, name_syntax) = match param.as_ref()? {
-                Either::Left(pat) => ("self".to_string(), pat.name()),
+                Either::Left(pat) => (pat.name()?, pat.name()),
                 Either::Right(pat) => match pat {
-                    ast::Pat::IdentPat(it) => (it.name()?.to_string(), it.name()),
+                    ast::Pat::IdentPat(it) => (it.name()?, it.name()),
                     _ => return None,
                 },
             };
             Some((name_syntax, param_name, arg, range))
         })
         .filter(|(_, param_name, arg, _)| {
-            !should_hide_param_name_hint(sema, &callable, param_name, arg)
+            !should_hide_param_name_hint(sema, &callable, &param_name.text(), arg)
         })
         .map(|(param, param_name, _, FileRange { range, .. })| {
             let mut linked_location = None;
@@ -53,10 +53,17 @@ pub(super) fn hints(
                 }
             }
 
+            let colon = if config.render_colons { ":" } else { "" };
+            let label =
+                InlayHintLabel::simple(format!("{param_name}{colon}"), None, linked_location);
             InlayHint {
                 range,
                 kind: InlayKind::Parameter,
-                label: InlayHintLabel::simple(param_name, None, linked_location),
+                label,
+                text_edit: None,
+                position: InlayHintPosition::Before,
+                pad_left: false,
+                pad_right: true,
             }
         });
 
diff --git a/src/tools/rust-analyzer/crates/ide/src/interpret_function.rs b/src/tools/rust-analyzer/crates/ide/src/interpret_function.rs
new file mode 100644
index 00000000000..cbcbb4b09db
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide/src/interpret_function.rs
@@ -0,0 +1,46 @@
+use hir::Semantics;
+use ide_db::base_db::SourceDatabaseExt;
+use ide_db::RootDatabase;
+use ide_db::{base_db::FilePosition, LineIndexDatabase};
+use std::{fmt::Write, time::Instant};
+use syntax::TextRange;
+use syntax::{algo::find_node_at_offset, ast, AstNode};
+
+// Feature: Interpret Function
+//
+// |===
+// | Editor  | Action Name
+//
+// | VS Code | **rust-analyzer: Interpret Function**
+// |===
+pub(crate) fn interpret_function(db: &RootDatabase, position: FilePosition) -> String {
+    let start_time = Instant::now();
+    let mut result = find_and_interpret(db, position)
+        .unwrap_or_else(|| "Not inside a function body".to_string());
+    let duration = Instant::now() - start_time;
+    writeln!(result, "").unwrap();
+    writeln!(result, "----------------------").unwrap();
+    writeln!(result, "  Finished in {}s", duration.as_secs_f32()).unwrap();
+    result
+}
+
+fn find_and_interpret(db: &RootDatabase, position: FilePosition) -> Option<String> {
+    let sema = Semantics::new(db);
+    let source_file = sema.parse(position.file_id);
+
+    let item = find_node_at_offset::<ast::Item>(source_file.syntax(), position.offset)?;
+    let def = match item {
+        ast::Item::Fn(it) => sema.to_def(&it)?,
+        _ => return None,
+    };
+    let span_formatter = |file_id, text_range: TextRange| {
+        let line_col = db.line_index(file_id).line_col(text_range.start());
+        let path = &db
+            .source_root(db.file_source_root(file_id))
+            .path_for_file(&file_id)
+            .map(|x| x.to_string());
+        let path = path.as_deref().unwrap_or("<unknown file>");
+        format!("file://{path}#{}:{}", line_col.line + 1, line_col.col)
+    };
+    Some(def.eval(db, span_formatter))
+}
diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs
index 078b66dd395..87e769e4230 100644
--- a/src/tools/rust-analyzer/crates/ide/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs
@@ -56,20 +56,24 @@ mod typing;
 mod view_crate_graph;
 mod view_hir;
 mod view_mir;
+mod interpret_function;
 mod view_item_tree;
 mod shuffle_crate_graph;
+mod fetch_crates;
 
-use std::sync::Arc;
+use std::ffi::OsStr;
 
 use cfg::CfgOptions;
+use fetch_crates::CrateInfo;
 use ide_db::{
     base_db::{
         salsa::{self, ParallelDatabase},
         CrateOrigin, Env, FileLoader, FileSet, SourceDatabase, VfsPath,
     },
-    symbol_index, LineIndexDatabase,
+    symbol_index, FxHashMap, FxIndexSet, LineIndexDatabase,
 };
 use syntax::SourceFile;
+use triomphe::Arc;
 
 use crate::navigation_target::{ToNav, TryToNav};
 
@@ -80,11 +84,14 @@ pub use crate::{
     file_structure::{StructureNode, StructureNodeKind},
     folding_ranges::{Fold, FoldKind},
     highlight_related::{HighlightRelatedConfig, HighlightedRange},
-    hover::{HoverAction, HoverConfig, HoverDocFormat, HoverGotoTypeData, HoverResult},
+    hover::{
+        HoverAction, HoverConfig, HoverDocFormat, HoverGotoTypeData, HoverResult,
+        MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind,
+    },
     inlay_hints::{
         AdjustmentHints, AdjustmentHintsMode, ClosureReturnTypeHints, DiscriminantHints, InlayHint,
-        InlayHintLabel, InlayHintLabelPart, InlayHintsConfig, InlayKind, InlayTooltip,
-        LifetimeElisionHints,
+        InlayHintLabel, InlayHintLabelPart, InlayHintPosition, InlayHintsConfig, InlayKind,
+        InlayTooltip, LifetimeElisionHints,
     },
     join_lines::JoinLinesConfig,
     markup::Markup,
@@ -154,7 +161,11 @@ impl AnalysisHost {
     }
 
     pub fn update_lru_capacity(&mut self, lru_capacity: Option<usize>) {
-        self.db.update_lru_capacity(lru_capacity);
+        self.db.update_parse_query_lru_capacity(lru_capacity);
+    }
+
+    pub fn update_lru_capacities(&mut self, lru_capacities: &FxHashMap<Box<str>, usize>) {
+        self.db.update_lru_capacities(lru_capacities);
     }
 
     /// Returns a snapshot of the current state, which you can query for
@@ -233,14 +244,14 @@ impl Analysis {
             None,
             None,
             cfg_options.clone(),
-            cfg_options,
+            None,
             Env::default(),
-            Ok(Vec::new()),
             false,
-            CrateOrigin::CratesIo { repo: None, name: None },
+            CrateOrigin::Local { repo: None, name: None },
             Err("Analysis::from_single_file has no target layout".into()),
+            None,
         );
-        change.change_file(file_id, Some(Arc::new(text)));
+        change.change_file(file_id, Some(Arc::from(text)));
         change.set_crate_graph(crate_graph);
         host.apply_change(change);
         (host.analysis(), file_id)
@@ -259,7 +270,7 @@ impl Analysis {
     }
 
     /// Gets the text of the source file.
-    pub fn file_text(&self, file_id: FileId) -> Cancellable<Arc<String>> {
+    pub fn file_text(&self, file_id: FileId) -> Cancellable<Arc<str>> {
         self.with_db(|db| db.file_text(file_id))
     }
 
@@ -313,6 +324,10 @@ impl Analysis {
         self.with_db(|db| view_mir::view_mir(db, position))
     }
 
+    pub fn interpret_function(&self, position: FilePosition) -> Cancellable<String> {
+        self.with_db(|db| interpret_function::interpret_function(db, position))
+    }
+
     pub fn view_item_tree(&self, file_id: FileId) -> Cancellable<String> {
         self.with_db(|db| view_item_tree::view_item_tree(db, file_id))
     }
@@ -322,6 +337,10 @@ impl Analysis {
         self.with_db(|db| view_crate_graph::view_crate_graph(db, full))
     }
 
+    pub fn fetch_crates(&self) -> Cancellable<FxIndexSet<CrateInfo>> {
+        self.with_db(|db| fetch_crates::fetch_crates(db))
+    }
+
     pub fn expand_macro(&self, position: FilePosition) -> Cancellable<Option<ExpandedMacro>> {
         self.with_db(|db| expand_macro::expand_macro(db, position))
     }
@@ -452,12 +471,19 @@ impl Analysis {
         self.with_db(|db| moniker::moniker(db, position))
     }
 
-    /// Return URL(s) for the documentation of the symbol under the cursor.
+    /// Returns URL(s) for the documentation of the symbol under the cursor.
+    /// # Arguments
+    /// * `position` - Position in the file.
+    /// * `target_dir` - Directory where the build output is storeda.
     pub fn external_docs(
         &self,
         position: FilePosition,
-    ) -> Cancellable<Option<doc_links::DocumentationLink>> {
-        self.with_db(|db| doc_links::external_docs(db, &position))
+        target_dir: Option<&OsStr>,
+        sysroot: Option<&OsStr>,
+    ) -> Cancellable<doc_links::DocumentationLinks> {
+        self.with_db(|db| {
+            doc_links::external_docs(db, &position, target_dir, sysroot).unwrap_or_default()
+        })
     }
 
     /// Computes parameter information at the given position.
@@ -508,6 +534,11 @@ impl Analysis {
         self.with_db(|db| db.crate_graph()[crate_id].edition)
     }
 
+    /// Returns true if this crate has `no_std` or `no_core` specified.
+    pub fn is_crate_no_std(&self, crate_id: CrateId) -> Cancellable<bool> {
+        self.with_db(|db| hir::db::DefDatabase::crate_def_map(db, crate_id).is_no_std())
+    }
+
     /// Returns the root file of the given crate.
     pub fn crate_root(&self, crate_id: CrateId) -> Cancellable<FileId> {
         self.with_db(|db| db.crate_graph()[crate_id].root_file_id)
diff --git a/src/tools/rust-analyzer/crates/ide/src/moniker.rs b/src/tools/rust-analyzer/crates/ide/src/moniker.rs
index 349e79ecfdd..0d57e63d29c 100644
--- a/src/tools/rust-analyzer/crates/ide/src/moniker.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/moniker.rs
@@ -1,7 +1,7 @@
 //! This module generates [moniker](https://microsoft.github.io/language-server-protocol/specifications/lsif/0.6.0/specification/#exportsImports)
 //! for LSIF and LSP.
 
-use hir::{AsAssocItem, AssocItemContainer, Crate, Name, Semantics};
+use hir::{AsAssocItem, AssocItemContainer, Crate, Semantics};
 use ide_db::{
     base_db::{CrateOrigin, FilePosition, LangCrateOrigin},
     defs::{Definition, IdentClass},
@@ -27,7 +27,7 @@ pub enum MonikerDescriptorKind {
 
 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
 pub struct MonikerDescriptor {
-    pub name: Name,
+    pub name: String,
     pub desc: MonikerDescriptorKind,
 }
 
@@ -41,11 +41,7 @@ impl ToString for MonikerIdentifier {
     fn to_string(&self) -> String {
         match self {
             MonikerIdentifier { description, crate_name } => {
-                format!(
-                    "{}::{}",
-                    crate_name,
-                    description.iter().map(|x| x.name.to_string()).join("::")
-                )
+                format!("{}::{}", crate_name, description.iter().map(|x| &x.name).join("::"))
             }
         }
     }
@@ -136,7 +132,10 @@ pub(crate) fn def_to_moniker(
     let krate = module.krate();
     let mut description = vec![];
     description.extend(module.path_to_root(db).into_iter().filter_map(|x| {
-        Some(MonikerDescriptor { name: x.name(db)?, desc: MonikerDescriptorKind::Namespace })
+        Some(MonikerDescriptor {
+            name: x.name(db)?.display(db).to_string(),
+            desc: MonikerDescriptorKind::Namespace,
+        })
     }));
 
     // Handle associated items within a trait
@@ -147,7 +146,7 @@ pub(crate) fn def_to_moniker(
                 // Because different traits can have functions with the same name,
                 // we have to include the trait name as part of the moniker for uniqueness.
                 description.push(MonikerDescriptor {
-                    name: trait_.name(db),
+                    name: trait_.name(db).display(db).to_string(),
                     desc: MonikerDescriptorKind::Type,
                 });
             }
@@ -156,14 +155,14 @@ pub(crate) fn def_to_moniker(
                 // we add both the struct name and the trait name to the path
                 if let Some(adt) = impl_.self_ty(db).as_adt() {
                     description.push(MonikerDescriptor {
-                        name: adt.name(db),
+                        name: adt.name(db).display(db).to_string(),
                         desc: MonikerDescriptorKind::Type,
                     });
                 }
 
                 if let Some(trait_) = impl_.trait_(db) {
                     description.push(MonikerDescriptor {
-                        name: trait_.name(db),
+                        name: trait_.name(db).display(db).to_string(),
                         desc: MonikerDescriptorKind::Type,
                     });
                 }
@@ -173,7 +172,7 @@ pub(crate) fn def_to_moniker(
 
     if let Definition::Field(it) = def {
         description.push(MonikerDescriptor {
-            name: it.parent_def(db).name(db),
+            name: it.parent_def(db).name(db).display(db).to_string(),
             desc: MonikerDescriptorKind::Type,
         });
     }
@@ -191,48 +190,63 @@ pub(crate) fn def_to_moniker(
                 return None;
             }
 
-            MonikerDescriptor { name: local.name(db), desc: MonikerDescriptorKind::Parameter }
-        }
-        Definition::Macro(m) => {
-            MonikerDescriptor { name: m.name(db), desc: MonikerDescriptorKind::Macro }
-        }
-        Definition::Function(f) => {
-            MonikerDescriptor { name: f.name(db), desc: MonikerDescriptorKind::Method }
-        }
-        Definition::Variant(v) => {
-            MonikerDescriptor { name: v.name(db), desc: MonikerDescriptorKind::Type }
-        }
-        Definition::Const(c) => {
-            MonikerDescriptor { name: c.name(db)?, desc: MonikerDescriptorKind::Term }
-        }
-        Definition::Trait(trait_) => {
-            MonikerDescriptor { name: trait_.name(db), desc: MonikerDescriptorKind::Type }
-        }
-        Definition::TraitAlias(ta) => {
-            MonikerDescriptor { name: ta.name(db), desc: MonikerDescriptorKind::Type }
-        }
-        Definition::TypeAlias(ta) => {
-            MonikerDescriptor { name: ta.name(db), desc: MonikerDescriptorKind::TypeParameter }
-        }
-        Definition::Module(m) => {
-            MonikerDescriptor { name: m.name(db)?, desc: MonikerDescriptorKind::Namespace }
-        }
-        Definition::BuiltinType(b) => {
-            MonikerDescriptor { name: b.name(), desc: MonikerDescriptorKind::Type }
+            MonikerDescriptor {
+                name: local.name(db).display(db).to_string(),
+                desc: MonikerDescriptorKind::Parameter,
+            }
         }
+        Definition::Macro(m) => MonikerDescriptor {
+            name: m.name(db).display(db).to_string(),
+            desc: MonikerDescriptorKind::Macro,
+        },
+        Definition::Function(f) => MonikerDescriptor {
+            name: f.name(db).display(db).to_string(),
+            desc: MonikerDescriptorKind::Method,
+        },
+        Definition::Variant(v) => MonikerDescriptor {
+            name: v.name(db).display(db).to_string(),
+            desc: MonikerDescriptorKind::Type,
+        },
+        Definition::Const(c) => MonikerDescriptor {
+            name: c.name(db)?.display(db).to_string(),
+            desc: MonikerDescriptorKind::Term,
+        },
+        Definition::Trait(trait_) => MonikerDescriptor {
+            name: trait_.name(db).display(db).to_string(),
+            desc: MonikerDescriptorKind::Type,
+        },
+        Definition::TraitAlias(ta) => MonikerDescriptor {
+            name: ta.name(db).display(db).to_string(),
+            desc: MonikerDescriptorKind::Type,
+        },
+        Definition::TypeAlias(ta) => MonikerDescriptor {
+            name: ta.name(db).display(db).to_string(),
+            desc: MonikerDescriptorKind::TypeParameter,
+        },
+        Definition::Module(m) => MonikerDescriptor {
+            name: m.name(db)?.display(db).to_string(),
+            desc: MonikerDescriptorKind::Namespace,
+        },
+        Definition::BuiltinType(b) => MonikerDescriptor {
+            name: b.name().display(db).to_string(),
+            desc: MonikerDescriptorKind::Type,
+        },
         Definition::SelfType(imp) => MonikerDescriptor {
-            name: imp.self_ty(db).as_adt()?.name(db),
+            name: imp.self_ty(db).as_adt()?.name(db).display(db).to_string(),
             desc: MonikerDescriptorKind::Type,
         },
-        Definition::Field(it) => {
-            MonikerDescriptor { name: it.name(db), desc: MonikerDescriptorKind::Term }
-        }
-        Definition::Adt(adt) => {
-            MonikerDescriptor { name: adt.name(db), desc: MonikerDescriptorKind::Type }
-        }
-        Definition::Static(s) => {
-            MonikerDescriptor { name: s.name(db), desc: MonikerDescriptorKind::Meta }
-        }
+        Definition::Field(it) => MonikerDescriptor {
+            name: it.name(db).display(db).to_string(),
+            desc: MonikerDescriptorKind::Term,
+        },
+        Definition::Adt(adt) => MonikerDescriptor {
+            name: adt.name(db).display(db).to_string(),
+            desc: MonikerDescriptorKind::Type,
+        },
+        Definition::Static(s) => MonikerDescriptor {
+            name: s.name(db).display(db).to_string(),
+            desc: MonikerDescriptorKind::Meta,
+        },
     };
 
     description.push(name_desc);
@@ -245,11 +259,17 @@ pub(crate) fn def_to_moniker(
         kind: if krate == from_crate { MonikerKind::Export } else { MonikerKind::Import },
         package_information: {
             let (name, repo, version) = match krate.origin(db) {
-                CrateOrigin::CratesIo { repo, name } => (
+                CrateOrigin::Library { repo, name } => (name, repo, krate.version(db)),
+                CrateOrigin::Local { repo, name } => (
                     name.unwrap_or(krate.display_name(db)?.canonical_name().to_string()),
                     repo,
                     krate.version(db),
                 ),
+                CrateOrigin::Rustc { name } => (
+                    name.clone(),
+                    Some("https://github.com/rust-lang/rust/".to_string()),
+                    Some(format!("https://github.com/rust-lang/rust/compiler/{name}",)),
+                ),
                 CrateOrigin::Lang(lang) => (
                     krate.display_name(db)?.canonical_name().to_string(),
                     Some("https://github.com/rust-lang/rust/".to_string()),
diff --git a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs
index 6aae82f9816..385c1b0c008 100644
--- a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs
@@ -4,8 +4,8 @@ use std::fmt;
 
 use either::Either;
 use hir::{
-    symbols::FileSymbol, AssocItem, Documentation, FieldSource, HasAttrs, HasSource, HirDisplay,
-    InFile, LocalSource, ModuleSource, Semantics,
+    symbols::FileSymbol, AssocItem, Documentation, FieldSource, HasAttrs, HasContainer, HasSource,
+    HirDisplay, HirFileId, InFile, LocalSource, ModuleSource,
 };
 use ide_db::{
     base_db::{FileId, FileRange},
@@ -15,7 +15,7 @@ use ide_db::{defs::Definition, RootDatabase};
 use stdx::never;
 use syntax::{
     ast::{self, HasName},
-    match_ast, AstNode, SmolStr, SyntaxNode, TextRange,
+    AstNode, SmolStr, SyntaxNode, TextRange,
 };
 
 /// `NavigationTarget` represents an element in the editor's UI which you can
@@ -45,6 +45,9 @@ pub struct NavigationTarget {
     pub container_name: Option<SmolStr>,
     pub description: Option<String>,
     pub docs: Option<Documentation>,
+    /// In addition to a `name` field, a `NavigationTarget` may also be aliased
+    /// In such cases we want a `NavigationTarget` to be accessible by its alias
+    pub alias: Option<SmolStr>,
 }
 
 impl fmt::Debug for NavigationTarget {
@@ -89,10 +92,9 @@ impl NavigationTarget {
 
     pub(crate) fn from_module_to_decl(db: &RootDatabase, module: hir::Module) -> NavigationTarget {
         let name = module.name(db).map(|it| it.to_smol_str()).unwrap_or_default();
-        if let Some(src @ InFile { value, .. }) = &module.declaration_source(db) {
-            let FileRange { file_id, range: full_range } = src.syntax().original_file_range(db);
-            let focus_range =
-                value.name().and_then(|it| orig_focus_range(db, src.file_id, it.syntax()));
+        if let Some(InFile { value, file_id }) = &module.declaration_source(db) {
+            let (file_id, full_range, focus_range) =
+                orig_range_with_focus(db, *file_id, value.syntax(), value.name());
             let mut res = NavigationTarget::from_syntax(
                 file_id,
                 name,
@@ -128,14 +130,15 @@ impl NavigationTarget {
     /// Allows `NavigationTarget` to be created from a `NameOwner`
     pub(crate) fn from_named(
         db: &RootDatabase,
-        node @ InFile { file_id, value }: InFile<&dyn ast::HasName>,
+        InFile { file_id, value }: InFile<&dyn ast::HasName>,
         kind: SymbolKind,
     ) -> NavigationTarget {
         let name = value.name().map(|it| it.text().into()).unwrap_or_else(|| "_".into());
-        let focus_range = value.name().and_then(|it| orig_focus_range(db, file_id, it.syntax()));
-        let FileRange { file_id, range } = node.map(|it| it.syntax()).original_file_range(db);
 
-        NavigationTarget::from_syntax(file_id, name, focus_range, range, kind)
+        let (file_id, full_range, focus_range) =
+            orig_range_with_focus(db, file_id, value.syntax(), value.name());
+
+        NavigationTarget::from_syntax(file_id, name, focus_range, full_range, kind)
     }
 
     fn from_syntax(
@@ -154,23 +157,43 @@ impl NavigationTarget {
             container_name: None,
             description: None,
             docs: None,
+            alias: None,
         }
     }
 }
 
 impl TryToNav for FileSymbol {
     fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> {
-        let full_range = self.loc.original_range(db)?;
-        let name_range = self.loc.original_name_range(db)?;
+        let full_range = self.loc.original_range(db);
+        let focus_range = self.loc.original_name_range(db).and_then(|it| {
+            if it.file_id == full_range.file_id {
+                Some(it.range)
+            } else {
+                None
+            }
+        });
 
         Some(NavigationTarget {
             file_id: full_range.file_id,
-            name: self.name.clone(),
-            kind: Some(self.kind.into()),
+            name: if self.is_alias { self.def.name(db)?.to_smol_str() } else { self.name.clone() },
+            alias: if self.is_alias { Some(self.name.clone()) } else { None },
+            kind: Some(hir::ModuleDefId::from(self.def).into()),
             full_range: full_range.range,
-            focus_range: Some(name_range.range),
+            focus_range,
             container_name: self.container_name.clone(),
-            description: description_from_symbol(db, self),
+            description: match self.def {
+                hir::ModuleDef::Module(it) => Some(it.display(db).to_string()),
+                hir::ModuleDef::Function(it) => Some(it.display(db).to_string()),
+                hir::ModuleDef::Adt(it) => Some(it.display(db).to_string()),
+                hir::ModuleDef::Variant(it) => Some(it.display(db).to_string()),
+                hir::ModuleDef::Const(it) => Some(it.display(db).to_string()),
+                hir::ModuleDef::Static(it) => Some(it.display(db).to_string()),
+                hir::ModuleDef::Trait(it) => Some(it.display(db).to_string()),
+                hir::ModuleDef::TraitAlias(it) => Some(it.display(db).to_string()),
+                hir::ModuleDef::TypeAlias(it) => Some(it.display(db).to_string()),
+                hir::ModuleDef::Macro(it) => Some(it.display(db).to_string()),
+                hir::ModuleDef::BuiltinType(_) => None,
+            },
             docs: None,
         })
     }
@@ -221,38 +244,80 @@ impl TryToNav for hir::ModuleDef {
     }
 }
 
-pub(crate) trait ToNavFromAst {
+pub(crate) trait ToNavFromAst: Sized {
     const KIND: SymbolKind;
+    fn container_name(self, db: &RootDatabase) -> Option<SmolStr> {
+        _ = db;
+        None
+    }
 }
+
+fn container_name(db: &RootDatabase, t: impl HasContainer) -> Option<SmolStr> {
+    match t.container(db) {
+        hir::ItemContainer::Trait(it) => Some(it.name(db).to_smol_str()),
+        // FIXME: Handle owners of blocks correctly here
+        hir::ItemContainer::Module(it) => it.name(db).map(|name| name.to_smol_str()),
+        _ => None,
+    }
+}
+
 impl ToNavFromAst for hir::Function {
     const KIND: SymbolKind = SymbolKind::Function;
+    fn container_name(self, db: &RootDatabase) -> Option<SmolStr> {
+        container_name(db, self)
+    }
 }
+
 impl ToNavFromAst for hir::Const {
     const KIND: SymbolKind = SymbolKind::Const;
+    fn container_name(self, db: &RootDatabase) -> Option<SmolStr> {
+        container_name(db, self)
+    }
 }
 impl ToNavFromAst for hir::Static {
     const KIND: SymbolKind = SymbolKind::Static;
+    fn container_name(self, db: &RootDatabase) -> Option<SmolStr> {
+        container_name(db, self)
+    }
 }
 impl ToNavFromAst for hir::Struct {
     const KIND: SymbolKind = SymbolKind::Struct;
+    fn container_name(self, db: &RootDatabase) -> Option<SmolStr> {
+        container_name(db, self)
+    }
 }
 impl ToNavFromAst for hir::Enum {
     const KIND: SymbolKind = SymbolKind::Enum;
+    fn container_name(self, db: &RootDatabase) -> Option<SmolStr> {
+        container_name(db, self)
+    }
 }
 impl ToNavFromAst for hir::Variant {
     const KIND: SymbolKind = SymbolKind::Variant;
 }
 impl ToNavFromAst for hir::Union {
     const KIND: SymbolKind = SymbolKind::Union;
+    fn container_name(self, db: &RootDatabase) -> Option<SmolStr> {
+        container_name(db, self)
+    }
 }
 impl ToNavFromAst for hir::TypeAlias {
     const KIND: SymbolKind = SymbolKind::TypeAlias;
+    fn container_name(self, db: &RootDatabase) -> Option<SmolStr> {
+        container_name(db, self)
+    }
 }
 impl ToNavFromAst for hir::Trait {
     const KIND: SymbolKind = SymbolKind::Trait;
+    fn container_name(self, db: &RootDatabase) -> Option<SmolStr> {
+        container_name(db, self)
+    }
 }
 impl ToNavFromAst for hir::TraitAlias {
     const KIND: SymbolKind = SymbolKind::TraitAlias;
+    fn container_name(self, db: &RootDatabase) -> Option<SmolStr> {
+        container_name(db, self)
+    }
 }
 
 impl<D> TryToNav for D
@@ -269,6 +334,7 @@ where
         );
         res.docs = self.docs(db);
         res.description = Some(self.display(db).to_string());
+        res.container_name = self.container_name(db);
         Some(res)
     }
 }
@@ -280,15 +346,11 @@ impl ToNav for hir::Module {
         let name = self.name(db).map(|it| it.to_smol_str()).unwrap_or_default();
         let (syntax, focus) = match &value {
             ModuleSource::SourceFile(node) => (node.syntax(), None),
-            ModuleSource::Module(node) => (
-                node.syntax(),
-                node.name().and_then(|it| orig_focus_range(db, file_id, it.syntax())),
-            ),
+            ModuleSource::Module(node) => (node.syntax(), node.name()),
             ModuleSource::BlockExpr(node) => (node.syntax(), None),
         };
-        let FileRange { file_id, range: full_range } =
-            InFile::new(file_id, syntax).original_file_range(db);
-        NavigationTarget::from_syntax(file_id, name, focus, full_range, SymbolKind::Module)
+        let (file_id, full_range, focus_range) = orig_range_with_focus(db, file_id, syntax, focus);
+        NavigationTarget::from_syntax(file_id, name, focus_range, full_range, SymbolKind::Module)
     }
 }
 
@@ -297,17 +359,14 @@ impl TryToNav for hir::Impl {
         let InFile { file_id, value } = self.source(db)?;
         let derive_attr = self.is_builtin_derive(db);
 
-        let focus_range = if derive_attr.is_some() {
-            None
-        } else {
-            value.self_ty().and_then(|ty| orig_focus_range(db, file_id, ty.syntax()))
-        };
+        let focus = if derive_attr.is_some() { None } else { value.self_ty() };
 
-        let FileRange { file_id, range: full_range } = match &derive_attr {
-            Some(attr) => attr.syntax().original_file_range(db),
-            None => InFile::new(file_id, value.syntax()).original_file_range(db),
+        let syntax = match &derive_attr {
+            Some(attr) => attr.value.syntax(),
+            None => value.syntax(),
         };
 
+        let (file_id, full_range, focus_range) = orig_range_with_focus(db, file_id, syntax, focus);
         Some(NavigationTarget::from_syntax(
             file_id,
             "impl".into(),
@@ -396,9 +455,8 @@ impl ToNav for LocalSource {
             Either::Left(bind_pat) => (bind_pat.syntax(), bind_pat.name()),
             Either::Right(it) => (it.syntax(), it.name()),
         };
-        let focus_range = name.and_then(|it| orig_focus_range(db, file_id, it.syntax()));
-        let FileRange { file_id, range: full_range } =
-            InFile::new(file_id, node).original_file_range(db);
+
+        let (file_id, full_range, focus_range) = orig_range_with_focus(db, file_id, node, name);
 
         let name = local.name(db).to_smol_str();
         let kind = if local.is_self(db) {
@@ -411,6 +469,7 @@ impl ToNav for LocalSource {
         NavigationTarget {
             file_id,
             name,
+            alias: None,
             kind: Some(kind),
             full_range,
             focus_range,
@@ -432,13 +491,13 @@ impl ToNav for hir::Label {
         let InFile { file_id, value } = self.source(db);
         let name = self.name(db).to_smol_str();
 
-        let range = |syntax: &_| InFile::new(file_id, syntax).original_file_range(db);
-        let FileRange { file_id, range: full_range } = range(value.syntax());
-        let focus_range = value.lifetime().map(|lt| range(lt.syntax()).range);
+        let (file_id, full_range, focus_range) =
+            orig_range_with_focus(db, file_id, value.syntax(), value.lifetime());
 
         NavigationTarget {
             file_id,
             name,
+            alias: None,
             kind: Some(SymbolKind::Label),
             full_range,
             focus_range,
@@ -463,22 +522,18 @@ impl TryToNav for hir::TypeParam {
             Either::Right(x) => Either::Right(x),
         };
 
-        let range = |syntax: &_| InFile::new(file_id, syntax).original_file_range(db);
-        let focus_range = |syntax: &_| InFile::new(file_id, syntax).original_file_range_opt(db);
-        let FileRange { file_id, range: full_range } = match &value {
-            Either::Left(type_param) => range(type_param.syntax()),
-            Either::Right(trait_) => trait_
-                .name()
-                .and_then(|name| focus_range(name.syntax()))
-                .unwrap_or_else(|| range(trait_.syntax())),
+        let syntax = match &value {
+            Either::Left(type_param) => type_param.syntax(),
+            Either::Right(trait_) => trait_.syntax(),
         };
-        let focus_range = value
-            .either(|it| it.name(), |it| it.name())
-            .and_then(|it| focus_range(it.syntax()))
-            .map(|it| it.range);
+        let focus = value.as_ref().either(|it| it.name(), |it| it.name());
+
+        let (file_id, full_range, focus_range) = orig_range_with_focus(db, file_id, syntax, focus);
+
         Some(NavigationTarget {
             file_id,
             name,
+            alias: None,
             kind: Some(SymbolKind::TypeParam),
             full_range,
             focus_range,
@@ -500,14 +555,15 @@ impl TryToNav for hir::LifetimeParam {
         let InFile { file_id, value } = self.source(db)?;
         let name = self.name(db).to_smol_str();
 
-        let FileRange { file_id, range: full_range } =
+        let FileRange { file_id, range } =
             InFile::new(file_id, value.syntax()).original_file_range(db);
         Some(NavigationTarget {
             file_id,
             name,
+            alias: None,
             kind: Some(SymbolKind::LifetimeParam),
-            full_range,
-            focus_range: Some(full_range),
+            full_range: range,
+            focus_range: Some(range),
             container_name: None,
             description: None,
             docs: None,
@@ -528,12 +584,12 @@ impl TryToNav for hir::ConstParam {
             }
         };
 
-        let focus_range = value.name().and_then(|it| orig_focus_range(db, file_id, it.syntax()));
-        let FileRange { file_id, range: full_range } =
-            InFile::new(file_id, value.syntax()).original_file_range(db);
+        let (file_id, full_range, focus_range) =
+            orig_range_with_focus(db, file_id, value.syntax(), value.name());
         Some(NavigationTarget {
             file_id,
             name,
+            alias: None,
             kind: Some(SymbolKind::ConstParam),
             full_range,
             focus_range,
@@ -544,38 +600,19 @@ impl TryToNav for hir::ConstParam {
     }
 }
 
-/// Get a description of a symbol.
-///
-/// e.g. `struct Name`, `enum Name`, `fn Name`
-pub(crate) fn description_from_symbol(db: &RootDatabase, symbol: &FileSymbol) -> Option<String> {
-    let sema = Semantics::new(db);
-    let node = symbol.loc.syntax(&sema)?;
-
-    match_ast! {
-        match node {
-            ast::Fn(it) => sema.to_def(&it).map(|it| it.display(db).to_string()),
-            ast::Struct(it) => sema.to_def(&it).map(|it| it.display(db).to_string()),
-            ast::Enum(it) => sema.to_def(&it).map(|it| it.display(db).to_string()),
-            ast::Trait(it) => sema.to_def(&it).map(|it| it.display(db).to_string()),
-            ast::TraitAlias(it) => sema.to_def(&it).map(|it| it.display(db).to_string()),
-            ast::Module(it) => sema.to_def(&it).map(|it| it.display(db).to_string()),
-            ast::TypeAlias(it) => sema.to_def(&it).map(|it| it.display(db).to_string()),
-            ast::Const(it) => sema.to_def(&it).map(|it| it.display(db).to_string()),
-            ast::Static(it) => sema.to_def(&it).map(|it| it.display(db).to_string()),
-            ast::RecordField(it) => sema.to_def(&it).map(|it| it.display(db).to_string()),
-            ast::Variant(it) => sema.to_def(&it).map(|it| it.display(db).to_string()),
-            ast::Union(it) => sema.to_def(&it).map(|it| it.display(db).to_string()),
-            _ => None,
-        }
-    }
-}
-
-fn orig_focus_range(
+fn orig_range_with_focus(
     db: &RootDatabase,
-    file_id: hir::HirFileId,
-    syntax: &SyntaxNode,
-) -> Option<TextRange> {
-    InFile::new(file_id, syntax).original_file_range_opt(db).map(|it| it.range)
+    hir_file: HirFileId,
+    value: &SyntaxNode,
+    name: Option<impl AstNode>,
+) -> (FileId, TextRange, Option<TextRange>) {
+    let FileRange { file_id, range: full_range } =
+        InFile::new(hir_file, value).original_file_range(db);
+    let focus_range = name
+        .and_then(|it| InFile::new(hir_file, it.syntax()).original_file_range_opt(db))
+        .and_then(|range| if range.file_id == file_id { Some(range.range) } else { None });
+
+    (file_id, full_range, focus_range)
 }
 
 #[cfg(test)]
diff --git a/src/tools/rust-analyzer/crates/ide/src/prime_caches.rs b/src/tools/rust-analyzer/crates/ide/src/prime_caches.rs
index 87b3ef380c5..d704d12a05b 100644
--- a/src/tools/rust-analyzer/crates/ide/src/prime_caches.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/prime_caches.rs
@@ -12,9 +12,8 @@ use ide_db::{
         salsa::{Database, ParallelDatabase, Snapshot},
         Cancelled, CrateGraph, CrateId, SourceDatabase, SourceDatabaseExt,
     },
-    FxIndexMap,
+    FxHashSet, FxIndexMap,
 };
-use stdx::hash::NoHashHashSet;
 
 use crate::RootDatabase;
 
@@ -81,7 +80,11 @@ pub(crate) fn parallel_prime_caches(
         for _ in 0..num_worker_threads {
             let worker = prime_caches_worker.clone();
             let db = db.snapshot();
-            std::thread::spawn(move || Cancelled::catch(|| worker(db)));
+
+            stdx::thread::Builder::new(stdx::thread::ThreadIntent::Worker)
+                .allow_leak(true)
+                .spawn(move || Cancelled::catch(|| worker(db)))
+                .expect("failed to spawn thread");
         }
 
         (work_sender, progress_receiver)
@@ -142,7 +145,7 @@ pub(crate) fn parallel_prime_caches(
     }
 }
 
-fn compute_crates_to_prime(db: &RootDatabase, graph: &CrateGraph) -> NoHashHashSet<CrateId> {
+fn compute_crates_to_prime(db: &RootDatabase, graph: &CrateGraph) -> FxHashSet<CrateId> {
     // We're only interested in the workspace crates and the `ImportMap`s of their direct
     // dependencies, though in practice the latter also compute the `DefMap`s.
     // We don't prime transitive dependencies because they're generally not visible in
diff --git a/src/tools/rust-analyzer/crates/ide/src/references.rs b/src/tools/rust-analyzer/crates/ide/src/references.rs
index 3684c1033f3..fdc5261ac38 100644
--- a/src/tools/rust-analyzer/crates/ide/src/references.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/references.rs
@@ -17,7 +17,7 @@ use ide_db::{
     RootDatabase,
 };
 use itertools::Itertools;
-use stdx::hash::NoHashHashMap;
+use nohash_hasher::IntMap;
 use syntax::{
     algo::find_node_at_offset,
     ast::{self, HasName},
@@ -31,7 +31,7 @@ use crate::{FilePosition, NavigationTarget, TryToNav};
 #[derive(Debug, Clone)]
 pub struct ReferenceSearchResult {
     pub declaration: Option<Declaration>,
-    pub references: NoHashHashMap<FileId, Vec<(TextRange, Option<ReferenceCategory>)>>,
+    pub references: IntMap<FileId, Vec<(TextRange, Option<ReferenceCategory>)>>,
 }
 
 #[derive(Debug, Clone)]
@@ -715,7 +715,7 @@ fn f() {
 }
 "#,
             expect![[r#"
-                Foo Struct FileId(1) 17..51 28..31
+                Foo Struct FileId(1) 17..51 28..31 foo
 
                 FileId(0) 53..56
                 FileId(2) 79..82
@@ -803,7 +803,7 @@ pub(super) struct Foo$0 {
 }
 "#,
             expect![[r#"
-                Foo Struct FileId(2) 0..41 18..21
+                Foo Struct FileId(2) 0..41 18..21 some
 
                 FileId(1) 20..23 Import
                 FileId(1) 47..50
@@ -1289,7 +1289,7 @@ trait Foo where Self$0 {
 impl Foo for () {}
 "#,
             expect![[r#"
-                Self TypeParam FileId(0) 6..9 6..9
+                Self TypeParam FileId(0) 0..44 6..9
 
                 FileId(0) 16..20
                 FileId(0) 37..41
@@ -1380,7 +1380,7 @@ fn foo<T: Bar>(_: impl Bar, _: &dyn Bar) {}
 trait Foo = where Self$0: ;
 "#,
             expect![[r#"
-                Self TypeParam FileId(0) 6..9 6..9
+                Self TypeParam FileId(0) 0..25 6..9
 
                 FileId(0) 18..22
             "#]],
@@ -1542,7 +1542,7 @@ fn f() {
                 FileId(0) 161..165
 
 
-                func Function FileId(0) 137..146 140..144
+                func Function FileId(0) 137..146 140..144 module
 
                 FileId(0) 181..185
             "#]],
@@ -1581,7 +1581,7 @@ trait Trait {
 }
 "#,
             expect![[r#"
-                func Function FileId(0) 48..87 51..55
+                func Function FileId(0) 48..87 51..55 Trait
 
                 FileId(0) 74..78
             "#]],
@@ -1692,7 +1692,7 @@ fn f<T: Trait>() {
 }
 "#,
             expect![[r#"
-                CONST Const FileId(0) 18..37 24..29
+                CONST Const FileId(0) 18..37 24..29 Trait
 
                 FileId(0) 71..76
                 FileId(0) 125..130
@@ -1721,7 +1721,7 @@ fn f<T: Trait>() {
 }
 "#,
             expect![[r#"
-                TypeAlias TypeAlias FileId(0) 18..33 23..32
+                TypeAlias TypeAlias FileId(0) 18..33 23..32 Trait
 
                 FileId(0) 66..75
                 FileId(0) 117..126
@@ -1750,7 +1750,7 @@ fn f<T: Trait>() {
 }
 "#,
             expect![[r#"
-                function Function FileId(0) 18..34 21..29
+                function Function FileId(0) 18..34 21..29 Trait
 
                 FileId(0) 65..73
                 FileId(0) 112..120
@@ -1894,7 +1894,7 @@ fn f<T: Trait>() {
 }
 "#,
             expect![[r#"
-                TypeAlias TypeAlias FileId(0) 18..33 23..32
+                TypeAlias TypeAlias FileId(0) 18..33 23..32 Trait
 
                 FileId(0) 66..75
                 FileId(0) 117..126
@@ -1950,7 +1950,7 @@ impl Foo for Bar {
 fn method() {}
 "#,
             expect![[r#"
-                method Function FileId(0) 16..39 19..25
+                method Function FileId(0) 16..39 19..25 Foo
 
                 FileId(0) 101..107
             "#]],
diff --git a/src/tools/rust-analyzer/crates/ide/src/runnables.rs b/src/tools/rust-analyzer/crates/ide/src/runnables.rs
index 8a8a9151c42..27ad63d820d 100644
--- a/src/tools/rust-analyzer/crates/ide/src/runnables.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/runnables.rs
@@ -349,8 +349,13 @@ pub(crate) fn runnable_mod(
     if !has_test_function_or_multiple_test_submodules(sema, &def) {
         return None;
     }
-    let path =
-        def.path_to_root(sema.db).into_iter().rev().filter_map(|it| it.name(sema.db)).join("::");
+    let path = def
+        .path_to_root(sema.db)
+        .into_iter()
+        .rev()
+        .filter_map(|it| it.name(sema.db))
+        .map(|it| it.display(sema.db).to_string())
+        .join("::");
 
     let attrs = def.attrs(sema.db);
     let cfg = attrs.cfg();
@@ -376,7 +381,7 @@ pub(crate) fn runnable_impl(
     } else {
         String::new()
     };
-    let mut test_id = format!("{adt_name}{params}");
+    let mut test_id = format!("{}{params}", adt_name.display(sema.db));
     test_id.retain(|c| c != ' ');
     let test_id = TestId::Path(test_id);
 
@@ -391,8 +396,13 @@ fn runnable_mod_outline_definition(
     if !has_test_function_or_multiple_test_submodules(sema, &def) {
         return None;
     }
-    let path =
-        def.path_to_root(sema.db).into_iter().rev().filter_map(|it| it.name(sema.db)).join("::");
+    let path = def
+        .path_to_root(sema.db)
+        .into_iter()
+        .rev()
+        .filter_map(|it| it.name(sema.db))
+        .map(|it| it.display(sema.db).to_string())
+        .join("::");
 
     let attrs = def.attrs(sema.db);
     let cfg = attrs.cfg();
@@ -430,7 +440,7 @@ fn module_def_doctest(db: &RootDatabase, def: Definition) -> Option<Runnable> {
         let mut path = String::new();
         def.canonical_module_path(db)?
             .flat_map(|it| it.name(db))
-            .for_each(|name| format_to!(path, "{}::", name));
+            .for_each(|name| format_to!(path, "{}::", name.display(db)));
         // This probably belongs to canonical_path?
         if let Some(assoc_item) = def.as_assoc_item(db) {
             if let hir::AssocItemContainer::Impl(imp) = assoc_item.container(db) {
@@ -438,17 +448,17 @@ fn module_def_doctest(db: &RootDatabase, def: Definition) -> Option<Runnable> {
                 if let Some(adt) = ty.as_adt() {
                     let name = adt.name(db);
                     let mut ty_args = ty.generic_parameters(db).peekable();
-                    format_to!(path, "{}", name);
+                    format_to!(path, "{}", name.display(db));
                     if ty_args.peek().is_some() {
                         format_to!(path, "<{}>", ty_args.format_with(",", |ty, cb| cb(&ty)));
                     }
-                    format_to!(path, "::{}", def_name);
+                    format_to!(path, "::{}", def_name.display(db));
                     path.retain(|c| c != ' ');
                     return Some(path);
                 }
             }
         }
-        format_to!(path, "{}", def_name);
+        format_to!(path, "{}", def_name.display(db));
         Some(path)
     })();
 
@@ -2232,14 +2242,14 @@ mod tests {
                             file_id: FileId(
                                 0,
                             ),
-                            full_range: 52..115,
-                            focus_range: 67..75,
-                            name: "foo_test",
+                            full_range: 121..185,
+                            focus_range: 136..145,
+                            name: "foo2_test",
                             kind: Function,
                         },
                         kind: Test {
                             test_id: Path(
-                                "tests::foo_test",
+                                "tests::foo2_test",
                             ),
                             attr: TestAttr {
                                 ignore: false,
@@ -2253,14 +2263,14 @@ mod tests {
                             file_id: FileId(
                                 0,
                             ),
-                            full_range: 121..185,
-                            focus_range: 136..145,
-                            name: "foo2_test",
+                            full_range: 52..115,
+                            focus_range: 67..75,
+                            name: "foo_test",
                             kind: Function,
                         },
                         kind: Test {
                             test_id: Path(
-                                "tests::foo2_test",
+                                "tests::foo_test",
                             ),
                             attr: TestAttr {
                                 ignore: false,
@@ -2579,6 +2589,7 @@ mod r#mod {
                             ),
                             full_range: 47..84,
                             name: "r#for",
+                            container_name: "r#mod",
                         },
                         kind: DocTest {
                             test_id: Path(
@@ -2595,6 +2606,7 @@ mod r#mod {
                             ),
                             full_range: 90..146,
                             name: "r#struct",
+                            container_name: "r#mod",
                         },
                         kind: DocTest {
                             test_id: Path(
diff --git a/src/tools/rust-analyzer/crates/ide/src/shuffle_crate_graph.rs b/src/tools/rust-analyzer/crates/ide/src/shuffle_crate_graph.rs
index e606072a823..f85700daf1f 100644
--- a/src/tools/rust-analyzer/crates/ide/src/shuffle_crate_graph.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/shuffle_crate_graph.rs
@@ -1,9 +1,8 @@
-use std::sync::Arc;
-
 use ide_db::{
-    base_db::{salsa::Durability, CrateGraph, SourceDatabase},
+    base_db::{salsa::Durability, CrateGraph, ProcMacros, SourceDatabase},
     FxHashMap, RootDatabase,
 };
+use triomphe::Arc;
 
 // Feature: Shuffle Crate Graph
 //
@@ -16,6 +15,7 @@ use ide_db::{
 // |===
 pub(crate) fn shuffle_crate_graph(db: &mut RootDatabase) {
     let crate_graph = db.crate_graph();
+    let proc_macros = db.proc_macros();
 
     let mut shuffled_ids = crate_graph.iter().collect::<Vec<_>>();
 
@@ -23,6 +23,7 @@ pub(crate) fn shuffle_crate_graph(db: &mut RootDatabase) {
     stdx::rand::shuffle(&mut shuffled_ids, |i| rng.rand_range(0..i as u32) as usize);
 
     let mut new_graph = CrateGraph::default();
+    let mut new_proc_macros = ProcMacros::default();
 
     let mut map = FxHashMap::default();
     for old_id in shuffled_ids.iter().copied() {
@@ -35,11 +36,12 @@ pub(crate) fn shuffle_crate_graph(db: &mut RootDatabase) {
             data.cfg_options.clone(),
             data.potential_cfg_options.clone(),
             data.env.clone(),
-            data.proc_macro.clone(),
             data.is_proc_macro,
             data.origin.clone(),
             data.target_layout.clone(),
+            data.channel,
         );
+        new_proc_macros.insert(new_id, proc_macros[&old_id].clone());
         map.insert(old_id, new_id);
     }
 
@@ -53,4 +55,5 @@ pub(crate) fn shuffle_crate_graph(db: &mut RootDatabase) {
     }
 
     db.set_crate_graph_with_durability(Arc::new(new_graph), Durability::HIGH);
+    db.set_proc_macros_with_durability(Arc::new(new_proc_macros), Durability::HIGH);
 }
diff --git a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs
index 4b2c139f6f4..7795be54e26 100644
--- a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs
@@ -15,8 +15,9 @@ use ide_db::{
 use stdx::format_to;
 use syntax::{
     algo,
-    ast::{self, HasArgList},
-    match_ast, AstNode, Direction, SyntaxElementChildren, SyntaxToken, TextRange, TextSize,
+    ast::{self, AstChildren, HasArgList},
+    match_ast, AstNode, Direction, NodeOrToken, SyntaxElementChildren, SyntaxNode, SyntaxToken,
+    TextRange, TextSize, T,
 };
 
 use crate::RootDatabase;
@@ -116,6 +117,20 @@ pub(crate) fn signature_help(db: &RootDatabase, position: FilePosition) -> Optio
                     }
                     return signature_help_for_tuple_struct_pat(&sema, tuple_pat, token);
                 },
+                ast::TuplePat(tuple_pat) => {
+                    let cursor_outside = tuple_pat.r_paren_token().as_ref() == Some(&token);
+                    if cursor_outside {
+                        continue;
+                    }
+                    return signature_help_for_tuple_pat(&sema, tuple_pat, token);
+                },
+                ast::TupleExpr(tuple_expr) => {
+                    let cursor_outside = tuple_expr.r_paren_token().as_ref() == Some(&token);
+                    if cursor_outside {
+                        continue;
+                    }
+                    return signature_help_for_tuple_expr(&sema, tuple_expr, token);
+                },
                 _ => (),
             }
         }
@@ -162,7 +177,7 @@ fn signature_help_for_call(
     match callable.kind() {
         hir::CallableKind::Function(func) => {
             res.doc = func.docs(db).map(|it| it.into());
-            format_to!(res.signature, "fn {}", func.name(db));
+            format_to!(res.signature, "fn {}", func.name(db).display(db));
             fn_params = Some(match callable.receiver_param(db) {
                 Some(_self) => func.params_without_self(db),
                 None => func.assoc_fn_params(db),
@@ -170,15 +185,15 @@ fn signature_help_for_call(
         }
         hir::CallableKind::TupleStruct(strukt) => {
             res.doc = strukt.docs(db).map(|it| it.into());
-            format_to!(res.signature, "struct {}", strukt.name(db));
+            format_to!(res.signature, "struct {}", strukt.name(db).display(db));
         }
         hir::CallableKind::TupleEnumVariant(variant) => {
             res.doc = variant.docs(db).map(|it| it.into());
             format_to!(
                 res.signature,
                 "enum {}::{}",
-                variant.parent_enum(db).name(db),
-                variant.name(db)
+                variant.parent_enum(db).name(db).display(db),
+                variant.name(db).display(db)
             );
         }
         hir::CallableKind::Closure | hir::CallableKind::FnPtr | hir::CallableKind::Other => (),
@@ -248,31 +263,31 @@ fn signature_help_for_generics(
     match generics_def {
         hir::GenericDef::Function(it) => {
             res.doc = it.docs(db).map(|it| it.into());
-            format_to!(res.signature, "fn {}", it.name(db));
+            format_to!(res.signature, "fn {}", it.name(db).display(db));
         }
         hir::GenericDef::Adt(hir::Adt::Enum(it)) => {
             res.doc = it.docs(db).map(|it| it.into());
-            format_to!(res.signature, "enum {}", it.name(db));
+            format_to!(res.signature, "enum {}", it.name(db).display(db));
         }
         hir::GenericDef::Adt(hir::Adt::Struct(it)) => {
             res.doc = it.docs(db).map(|it| it.into());
-            format_to!(res.signature, "struct {}", it.name(db));
+            format_to!(res.signature, "struct {}", it.name(db).display(db));
         }
         hir::GenericDef::Adt(hir::Adt::Union(it)) => {
             res.doc = it.docs(db).map(|it| it.into());
-            format_to!(res.signature, "union {}", it.name(db));
+            format_to!(res.signature, "union {}", it.name(db).display(db));
         }
         hir::GenericDef::Trait(it) => {
             res.doc = it.docs(db).map(|it| it.into());
-            format_to!(res.signature, "trait {}", it.name(db));
+            format_to!(res.signature, "trait {}", it.name(db).display(db));
         }
         hir::GenericDef::TraitAlias(it) => {
             res.doc = it.docs(db).map(|it| it.into());
-            format_to!(res.signature, "trait {}", it.name(db));
+            format_to!(res.signature, "trait {}", it.name(db).display(db));
         }
         hir::GenericDef::TypeAlias(it) => {
             res.doc = it.docs(db).map(|it| it.into());
-            format_to!(res.signature, "type {}", it.name(db));
+            format_to!(res.signature, "type {}", it.name(db).display(db));
         }
         hir::GenericDef::Variant(it) => {
             // In paths, generics of an enum can be specified *after* one of its variants.
@@ -280,7 +295,7 @@ fn signature_help_for_generics(
             // We'll use the signature of the enum, but include the docs of the variant.
             res.doc = it.docs(db).map(|it| it.into());
             let enum_ = it.parent_enum(db);
-            format_to!(res.signature, "enum {}", enum_.name(db));
+            format_to!(res.signature, "enum {}", enum_.name(db).display(db));
             generics_def = enum_.into();
         }
         // These don't have generic args that can be specified
@@ -395,24 +410,26 @@ fn signature_help_for_tuple_struct_pat(
     pat: ast::TupleStructPat,
     token: SyntaxToken,
 ) -> Option<SignatureHelp> {
-    let rest_pat = pat.fields().find(|it| matches!(it, ast::Pat::RestPat(_)));
-    let is_left_of_rest_pat =
-        rest_pat.map_or(true, |it| token.text_range().start() < it.syntax().text_range().end());
-
+    let path = pat.path()?;
+    let path_res = sema.resolve_path(&path)?;
     let mut res = SignatureHelp {
         doc: None,
         signature: String::new(),
         parameters: vec![],
         active_parameter: None,
     };
-
     let db = sema.db;
-    let path_res = sema.resolve_path(&pat.path()?)?;
+
     let fields: Vec<_> = if let PathResolution::Def(ModuleDef::Variant(variant)) = path_res {
         let en = variant.parent_enum(db);
 
         res.doc = en.docs(db).map(|it| it.into());
-        format_to!(res.signature, "enum {}::{} (", en.name(db), variant.name(db));
+        format_to!(
+            res.signature,
+            "enum {}::{} (",
+            en.name(db).display(db),
+            variant.name(db).display(db)
+        );
         variant.fields(db)
     } else {
         let adt = match path_res {
@@ -424,36 +441,78 @@ fn signature_help_for_tuple_struct_pat(
         match adt {
             hir::Adt::Struct(it) => {
                 res.doc = it.docs(db).map(|it| it.into());
-                format_to!(res.signature, "struct {} (", it.name(db));
+                format_to!(res.signature, "struct {} (", it.name(db).display(db));
                 it.fields(db)
             }
             _ => return None,
         }
     };
-    let commas = pat
-        .syntax()
-        .children_with_tokens()
-        .filter_map(syntax::NodeOrToken::into_token)
-        .filter(|t| t.kind() == syntax::T![,]);
-    res.active_parameter = Some(if is_left_of_rest_pat {
-        commas.take_while(|t| t.text_range().start() <= token.text_range().start()).count()
-    } else {
-        let n_commas = commas
-            .collect::<Vec<_>>()
-            .into_iter()
-            .rev()
-            .take_while(|t| t.text_range().start() > token.text_range().start())
-            .count();
-        fields.len().saturating_sub(1).saturating_sub(n_commas)
-    });
+    Some(signature_help_for_tuple_pat_ish(
+        db,
+        res,
+        pat.syntax(),
+        token,
+        pat.fields(),
+        fields.into_iter().map(|it| it.ty(db)),
+    ))
+}
+
+fn signature_help_for_tuple_pat(
+    sema: &Semantics<'_, RootDatabase>,
+    pat: ast::TuplePat,
+    token: SyntaxToken,
+) -> Option<SignatureHelp> {
+    let db = sema.db;
+    let field_pats = pat.fields();
+    let pat = pat.into();
+    let ty = sema.type_of_pat(&pat)?;
+    let fields = ty.original.tuple_fields(db);
+
+    Some(signature_help_for_tuple_pat_ish(
+        db,
+        SignatureHelp {
+            doc: None,
+            signature: String::from('('),
+            parameters: vec![],
+            active_parameter: None,
+        },
+        pat.syntax(),
+        token,
+        field_pats,
+        fields.into_iter(),
+    ))
+}
 
+fn signature_help_for_tuple_expr(
+    sema: &Semantics<'_, RootDatabase>,
+    expr: ast::TupleExpr,
+    token: SyntaxToken,
+) -> Option<SignatureHelp> {
+    let active_parameter = Some(
+        expr.syntax()
+            .children_with_tokens()
+            .filter_map(NodeOrToken::into_token)
+            .filter(|t| t.kind() == T![,])
+            .take_while(|t| t.text_range().start() <= token.text_range().start())
+            .count(),
+    );
+
+    let db = sema.db;
+    let mut res = SignatureHelp {
+        doc: None,
+        signature: String::from('('),
+        parameters: vec![],
+        active_parameter,
+    };
+    let expr = sema.type_of_expr(&expr.into())?;
+    let fields = expr.original.tuple_fields(db);
     let mut buf = String::new();
-    for ty in fields.into_iter().map(|it| it.ty(db)) {
+    for ty in fields {
         format_to!(buf, "{}", ty.display_truncated(db, Some(20)));
         res.push_call_param(&buf);
         buf.clear();
     }
-    res.signature.push_str(")");
+    res.signature.push(')');
     Some(res)
 }
 
@@ -465,8 +524,8 @@ fn signature_help_for_record_(
     token: SyntaxToken,
 ) -> Option<SignatureHelp> {
     let active_parameter = field_list_children
-        .filter_map(syntax::NodeOrToken::into_token)
-        .filter(|t| t.kind() == syntax::T![,])
+        .filter_map(NodeOrToken::into_token)
+        .filter(|t| t.kind() == T![,])
         .take_while(|t| t.text_range().start() <= token.text_range().start())
         .count();
 
@@ -486,7 +545,12 @@ fn signature_help_for_record_(
         let en = variant.parent_enum(db);
 
         res.doc = en.docs(db).map(|it| it.into());
-        format_to!(res.signature, "enum {}::{} {{ ", en.name(db), variant.name(db));
+        format_to!(
+            res.signature,
+            "enum {}::{} {{ ",
+            en.name(db).display(db),
+            variant.name(db).display(db)
+        );
     } else {
         let adt = match path_res {
             PathResolution::SelfType(imp) => imp.self_ty(db).as_adt()?,
@@ -498,12 +562,12 @@ fn signature_help_for_record_(
             hir::Adt::Struct(it) => {
                 fields = it.fields(db);
                 res.doc = it.docs(db).map(|it| it.into());
-                format_to!(res.signature, "struct {} {{ ", it.name(db));
+                format_to!(res.signature, "struct {} {{ ", it.name(db).display(db));
             }
             hir::Adt::Union(it) => {
                 fields = it.fields(db);
                 res.doc = it.docs(db).map(|it| it.into());
-                format_to!(res.signature, "union {} {{ ", it.name(db));
+                format_to!(res.signature, "union {} {{ ", it.name(db).display(db));
             }
             _ => return None,
         }
@@ -514,7 +578,7 @@ fn signature_help_for_record_(
     let mut buf = String::new();
     for (field, ty) in fields2 {
         let name = field.name(db);
-        format_to!(buf, "{name}: {}", ty.display_truncated(db, Some(20)));
+        format_to!(buf, "{}: {}", name.display(db), ty.display_truncated(db, Some(20)));
         res.push_record_field(&buf);
         buf.clear();
 
@@ -524,7 +588,7 @@ fn signature_help_for_record_(
     }
     for (name, field) in fields {
         let Some(field) = field else { continue };
-        format_to!(buf, "{name}: {}", field.ty(db).display_truncated(db, Some(20)));
+        format_to!(buf, "{}: {}", name.display(db), field.ty(db).display_truncated(db, Some(20)));
         res.push_record_field(&buf);
         buf.clear();
     }
@@ -532,6 +596,46 @@ fn signature_help_for_record_(
     Some(res)
 }
 
+fn signature_help_for_tuple_pat_ish(
+    db: &RootDatabase,
+    mut res: SignatureHelp,
+    pat: &SyntaxNode,
+    token: SyntaxToken,
+    mut field_pats: AstChildren<ast::Pat>,
+    fields: impl ExactSizeIterator<Item = hir::Type>,
+) -> SignatureHelp {
+    let rest_pat = field_pats.find(|it| matches!(it, ast::Pat::RestPat(_)));
+    let is_left_of_rest_pat =
+        rest_pat.map_or(true, |it| token.text_range().start() < it.syntax().text_range().end());
+
+    let commas = pat
+        .children_with_tokens()
+        .filter_map(NodeOrToken::into_token)
+        .filter(|t| t.kind() == T![,]);
+
+    res.active_parameter = {
+        Some(if is_left_of_rest_pat {
+            commas.take_while(|t| t.text_range().start() <= token.text_range().start()).count()
+        } else {
+            let n_commas = commas
+                .collect::<Vec<_>>()
+                .into_iter()
+                .rev()
+                .take_while(|t| t.text_range().start() > token.text_range().start())
+                .count();
+            fields.len().saturating_sub(1).saturating_sub(n_commas)
+        })
+    };
+
+    let mut buf = String::new();
+    for ty in fields {
+        format_to!(buf, "{}", ty.display_truncated(db, Some(20)));
+        res.push_call_param(&buf);
+        buf.clear();
+    }
+    res.signature.push_str(")");
+    res
+}
 #[cfg(test)]
 mod tests {
     use std::iter;
@@ -1228,6 +1332,24 @@ fn main() {
     }
 
     #[test]
+    fn call_info_for_fn_def_over_reference() {
+        check(
+            r#"
+struct S;
+fn foo(s: S) -> i32 { 92 }
+fn main() {
+    let bar = &&&&&foo;
+    bar($0);
+}
+        "#,
+            expect![[r#"
+                fn foo(s: S) -> i32
+                       ^^^^
+            "#]],
+        )
+    }
+
+    #[test]
     fn call_info_for_fn_ptr() {
         check(
             r#"
@@ -1823,4 +1945,290 @@ fn main() {
             "#]],
         );
     }
+
+    #[test]
+    fn test_tuple_expr_free() {
+        check(
+            r#"
+fn main() {
+    (0$0, 1, 3);
+}
+"#,
+            expect![[r#"
+                (i32, i32, i32)
+                 ^^^  ---  ---
+            "#]],
+        );
+        check(
+            r#"
+fn main() {
+    ($0 1, 3);
+}
+"#,
+            expect![[r#"
+                (i32, i32)
+                 ^^^  ---
+            "#]],
+        );
+        check(
+            r#"
+fn main() {
+    (1, 3 $0);
+}
+"#,
+            expect![[r#"
+                (i32, i32)
+                 ---  ^^^
+            "#]],
+        );
+        check(
+            r#"
+fn main() {
+    (1, 3 $0,);
+}
+"#,
+            expect![[r#"
+                (i32, i32)
+                 ---  ^^^
+            "#]],
+        );
+    }
+
+    #[test]
+    fn test_tuple_expr_expected() {
+        check(
+            r#"
+fn main() {
+    let _: (&str, u32, u32)= ($0, 1, 3);
+}
+"#,
+            expect![[r#"
+                (&str, u32, u32)
+                 ^^^^  ---  ---
+            "#]],
+        );
+        // FIXME: Should typeck report a 4-ary tuple for the expression here?
+        check(
+            r#"
+fn main() {
+    let _: (&str, u32, u32, u32) = ($0, 1, 3);
+}
+"#,
+            expect![[r#"
+                (&str, u32, u32)
+                 ^^^^  ---  ---
+            "#]],
+        );
+        check(
+            r#"
+fn main() {
+    let _: (&str, u32, u32)= ($0, 1, 3, 5);
+}
+"#,
+            expect![[r#"
+                (&str, u32, u32, i32)
+                 ^^^^  ---  ---  ---
+            "#]],
+        );
+    }
+
+    #[test]
+    fn test_tuple_pat_free() {
+        check(
+            r#"
+fn main() {
+    let ($0, 1, 3);
+}
+"#,
+            expect![[r#"
+                ({unknown}, i32, i32)
+                 ^^^^^^^^^  ---  ---
+            "#]],
+        );
+        check(
+            r#"
+fn main() {
+    let (0$0, 1, 3);
+}
+"#,
+            expect![[r#"
+                (i32, i32, i32)
+                 ^^^  ---  ---
+            "#]],
+        );
+        check(
+            r#"
+fn main() {
+    let ($0 1, 3);
+}
+"#,
+            expect![[r#"
+                (i32, i32)
+                 ^^^  ---
+            "#]],
+        );
+        check(
+            r#"
+fn main() {
+    let (1, 3 $0);
+}
+"#,
+            expect![[r#"
+                (i32, i32)
+                 ---  ^^^
+            "#]],
+        );
+        check(
+            r#"
+fn main() {
+    let (1, 3 $0,);
+}
+"#,
+            expect![[r#"
+                (i32, i32)
+                 ---  ^^^
+            "#]],
+        );
+        check(
+            r#"
+fn main() {
+    let (1, 3 $0, ..);
+}
+"#,
+            expect![[r#"
+                (i32, i32)
+                 ---  ^^^
+            "#]],
+        );
+        check(
+            r#"
+fn main() {
+    let (1, 3, .., $0);
+}
+"#,
+            // FIXME: This is wrong, this should not mark the last as active
+            expect![[r#"
+                (i32, i32)
+                 ---  ^^^
+            "#]],
+        );
+    }
+
+    #[test]
+    fn test_tuple_pat_expected() {
+        check(
+            r#"
+fn main() {
+    let (0$0, 1, 3): (i32, i32, i32);
+}
+"#,
+            expect![[r#"
+                (i32, i32, i32)
+                 ^^^  ---  ---
+            "#]],
+        );
+        check(
+            r#"
+fn main() {
+    let ($0, 1, 3): (i32, i32, i32);
+}
+"#,
+            expect![[r#"
+                (i32, i32, i32)
+                 ^^^  ---  ---
+            "#]],
+        );
+        check(
+            r#"
+fn main() {
+    let (1, 3 $0): (i32,);
+}
+"#,
+            expect![[r#"
+                (i32, i32)
+                 ---  ^^^
+            "#]],
+        );
+        check(
+            r#"
+fn main() {
+    let (1, 3 $0, ..): (i32, i32, i32, i32);
+}
+"#,
+            expect![[r#"
+                (i32, i32, i32, i32)
+                 ---  ^^^  ---  ---
+            "#]],
+        );
+        check(
+            r#"
+fn main() {
+    let (1, 3, .., $0): (i32, i32, i32);
+}
+"#,
+            expect![[r#"
+                (i32, i32, i32)
+                 ---  ---  ^^^
+            "#]],
+        );
+    }
+    #[test]
+    fn test_tuple_pat_expected_inferred() {
+        check(
+            r#"
+fn main() {
+    let (0$0, 1, 3) = (1, 2 ,3);
+}
+"#,
+            expect![[r#"
+                (i32, i32, i32)
+                 ^^^  ---  ---
+            "#]],
+        );
+        check(
+            r#"
+fn main() {
+    let ($0 1, 3) = (1, 2, 3);
+}
+"#,
+            // FIXME: Should typeck report a 3-ary tuple for the pattern here?
+            expect![[r#"
+                (i32, i32)
+                 ^^^  ---
+            "#]],
+        );
+        check(
+            r#"
+fn main() {
+    let (1, 3 $0) = (1,);
+}
+"#,
+            expect![[r#"
+                (i32, i32)
+                 ---  ^^^
+            "#]],
+        );
+        check(
+            r#"
+fn main() {
+    let (1, 3 $0, ..) = (1, 2, 3, 4);
+}
+"#,
+            expect![[r#"
+                (i32, i32, i32, i32)
+                 ---  ^^^  ---  ---
+            "#]],
+        );
+        check(
+            r#"
+fn main() {
+    let (1, 3, .., $0) = (1, 2, 3);
+}
+"#,
+            expect![[r#"
+                (i32, i32, i32)
+                 ---  ---  ^^^
+            "#]],
+        );
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/ide/src/ssr.rs b/src/tools/rust-analyzer/crates/ide/src/ssr.rs
index 497eb1cc130..deaf3c9c416 100644
--- a/src/tools/rust-analyzer/crates/ide/src/ssr.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/ssr.rs
@@ -56,8 +56,6 @@ pub(crate) fn ssr_assists(
 
 #[cfg(test)]
 mod tests {
-    use std::sync::Arc;
-
     use expect_test::expect;
     use ide_assists::{Assist, AssistResolveStrategy};
     use ide_db::{
@@ -65,6 +63,7 @@ mod tests {
         symbol_index::SymbolsDatabase,
         FxHashSet, RootDatabase,
     };
+    use triomphe::Arc;
 
     use super::ssr_assists;
 
diff --git a/src/tools/rust-analyzer/crates/ide/src/static_index.rs b/src/tools/rust-analyzer/crates/ide/src/static_index.rs
index c97691b14a5..3e3d9f8f85c 100644
--- a/src/tools/rust-analyzer/crates/ide/src/static_index.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/static_index.rs
@@ -118,9 +118,11 @@ impl StaticIndex<'_> {
                     adjustment_hints_hide_outside_unsafe: false,
                     hide_named_constructor_hints: false,
                     hide_closure_initialization_hints: false,
+                    closure_style: hir::ClosureStyle::ImplFn,
                     param_names_for_lifetime_elision_hints: false,
                     binding_mode_hints: false,
                     max_length: Some(25),
+                    closure_capture_hints: false,
                     closing_brace_hints_min_lines: Some(25),
                 },
                 file_id,
@@ -136,10 +138,10 @@ impl StaticIndex<'_> {
         });
         let hover_config = HoverConfig {
             links_in_hover: true,
+            memory_layout: None,
             documentation: true,
             keywords: true,
             format: crate::HoverDocFormat::Markdown,
-            interpret_tests: false,
         };
         let tokens = tokens.filter(|token| {
             matches!(
diff --git a/src/tools/rust-analyzer/crates/ide/src/status.rs b/src/tools/rust-analyzer/crates/ide/src/status.rs
index 7ce782f93be..d2c77e2dc79 100644
--- a/src/tools/rust-analyzer/crates/ide/src/status.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/status.rs
@@ -1,9 +1,18 @@
-use std::{fmt, sync::Arc};
+use std::{fmt, marker::PhantomData};
 
-use hir::{ExpandResult, MacroFile};
-use ide_db::base_db::{
-    salsa::debug::{DebugQueryTable, TableEntry},
-    CrateId, FileId, FileTextQuery, SourceDatabase, SourceRootId,
+use hir::{
+    db::{AstIdMapQuery, AttrsQuery, BlockDefMapQuery, ParseMacroExpansionQuery},
+    Attr, Attrs, ExpandResult, MacroFile, Module,
+};
+use ide_db::{
+    base_db::{
+        salsa::{
+            debug::{DebugQueryTable, TableEntry},
+            Query, QueryTable,
+        },
+        CrateId, FileId, FileTextQuery, ParseQuery, SourceDatabase, SourceRootId,
+    },
+    symbol_index::ModuleSymbolsQuery,
 };
 use ide_db::{
     symbol_index::{LibrarySymbolsQuery, SymbolIndex},
@@ -14,13 +23,7 @@ use profile::{memory_usage, Bytes};
 use std::env;
 use stdx::format_to;
 use syntax::{ast, Parse, SyntaxNode};
-
-fn syntax_tree_stats(db: &RootDatabase) -> SyntaxTreeStats {
-    ide_db::base_db::ParseQuery.in_db(db).entries::<SyntaxTreeStats>()
-}
-fn macro_syntax_tree_stats(db: &RootDatabase) -> SyntaxTreeStats {
-    hir::db::ParseMacroExpansionQuery.in_db(db).entries::<SyntaxTreeStats>()
-}
+use triomphe::Arc;
 
 // Feature: Status
 //
@@ -34,15 +37,22 @@ fn macro_syntax_tree_stats(db: &RootDatabase) -> SyntaxTreeStats {
 // image::https://user-images.githubusercontent.com/48062697/113065584-05f34500-91b1-11eb-98cc-5c196f76be7f.gif[]
 pub(crate) fn status(db: &RootDatabase, file_id: Option<FileId>) -> String {
     let mut buf = String::new();
-    format_to!(buf, "{}\n", FileTextQuery.in_db(db).entries::<FilesStats>());
-    format_to!(buf, "{}\n", LibrarySymbolsQuery.in_db(db).entries::<LibrarySymbolsStats>());
-    format_to!(buf, "{}\n", syntax_tree_stats(db));
-    format_to!(buf, "{} (Macros)\n", macro_syntax_tree_stats(db));
+
+    format_to!(buf, "{}\n", collect_query(FileTextQuery.in_db(db)));
+    format_to!(buf, "{}\n", collect_query(ParseQuery.in_db(db)));
+    format_to!(buf, "{}\n", collect_query(ParseMacroExpansionQuery.in_db(db)));
+    format_to!(buf, "{}\n", collect_query(LibrarySymbolsQuery.in_db(db)));
+    format_to!(buf, "{}\n", collect_query(ModuleSymbolsQuery.in_db(db)));
     format_to!(buf, "{} in total\n", memory_usage());
     if env::var("RA_COUNT").is_ok() {
         format_to!(buf, "\nCounts:\n{}", profile::countme::get_all());
     }
 
+    format_to!(buf, "\nDebug info:\n");
+    format_to!(buf, "{}\n", collect_query(AttrsQuery.in_db(db)));
+    format_to!(buf, "{} ast id maps\n", collect_query_count(AstIdMapQuery.in_db(db)));
+    format_to!(buf, "{} block def maps\n", collect_query_count(BlockDefMapQuery.in_db(db)));
+
     if let Some(file_id) = file_id {
         format_to!(buf, "\nFile info:\n");
         let crates = crate::parent_module::crates_for(db, file_id);
@@ -52,8 +62,8 @@ pub(crate) fn status(db: &RootDatabase, file_id: Option<FileId>) -> String {
         let crate_graph = db.crate_graph();
         for krate in crates {
             let display_crate = |krate: CrateId| match &crate_graph[krate].display_name {
-                Some(it) => format!("{it}({krate:?})"),
-                None => format!("{krate:?}"),
+                Some(it) => format!("{it}({})", krate.into_raw()),
+                None => format!("{}", krate.into_raw()),
             };
             format_to!(buf, "Crate: {}\n", display_crate(krate));
             let deps = crate_graph[krate]
@@ -68,6 +78,82 @@ pub(crate) fn status(db: &RootDatabase, file_id: Option<FileId>) -> String {
     buf.trim().to_string()
 }
 
+fn collect_query<'q, Q>(table: QueryTable<'q, Q>) -> <Q as QueryCollect>::Collector
+where
+    QueryTable<'q, Q>: DebugQueryTable,
+    Q: QueryCollect,
+    <Q as Query>::Storage: 'q,
+    <Q as QueryCollect>::Collector: StatCollect<
+        <QueryTable<'q, Q> as DebugQueryTable>::Key,
+        <QueryTable<'q, Q> as DebugQueryTable>::Value,
+    >,
+{
+    struct StatCollectorWrapper<C>(C);
+    impl<C: StatCollect<K, V>, K, V> FromIterator<TableEntry<K, V>> for StatCollectorWrapper<C> {
+        fn from_iter<T>(iter: T) -> StatCollectorWrapper<C>
+        where
+            T: IntoIterator<Item = TableEntry<K, V>>,
+        {
+            let mut res = C::default();
+            for entry in iter {
+                res.collect_entry(entry.key, entry.value);
+            }
+            StatCollectorWrapper(res)
+        }
+    }
+    table.entries::<StatCollectorWrapper<<Q as QueryCollect>::Collector>>().0
+}
+
+fn collect_query_count<'q, Q>(table: QueryTable<'q, Q>) -> usize
+where
+    QueryTable<'q, Q>: DebugQueryTable,
+    Q: Query,
+    <Q as Query>::Storage: 'q,
+{
+    struct EntryCounter(usize);
+    impl<K, V> FromIterator<TableEntry<K, V>> for EntryCounter {
+        fn from_iter<T>(iter: T) -> EntryCounter
+        where
+            T: IntoIterator<Item = TableEntry<K, V>>,
+        {
+            EntryCounter(iter.into_iter().count())
+        }
+    }
+    table.entries::<EntryCounter>().0
+}
+
+trait QueryCollect: Query {
+    type Collector;
+}
+
+impl QueryCollect for LibrarySymbolsQuery {
+    type Collector = SymbolsStats<SourceRootId>;
+}
+
+impl QueryCollect for ParseQuery {
+    type Collector = SyntaxTreeStats<false>;
+}
+
+impl QueryCollect for ParseMacroExpansionQuery {
+    type Collector = SyntaxTreeStats<true>;
+}
+
+impl QueryCollect for FileTextQuery {
+    type Collector = FilesStats;
+}
+
+impl QueryCollect for ModuleSymbolsQuery {
+    type Collector = SymbolsStats<Module>;
+}
+
+impl QueryCollect for AttrsQuery {
+    type Collector = AttrsStats;
+}
+
+trait StatCollect<K, V>: Default {
+    fn collect_entry(&mut self, key: K, value: Option<V>);
+}
+
 #[derive(Default)]
 struct FilesStats {
     total: usize,
@@ -80,85 +166,98 @@ impl fmt::Display for FilesStats {
     }
 }
 
-impl FromIterator<TableEntry<FileId, Arc<String>>> for FilesStats {
-    fn from_iter<T>(iter: T) -> FilesStats
-    where
-        T: IntoIterator<Item = TableEntry<FileId, Arc<String>>>,
-    {
-        let mut res = FilesStats::default();
-        for entry in iter {
-            res.total += 1;
-            res.size += entry.value.unwrap().len();
-        }
-        res
+impl StatCollect<FileId, Arc<str>> for FilesStats {
+    fn collect_entry(&mut self, _: FileId, value: Option<Arc<str>>) {
+        self.total += 1;
+        self.size += value.unwrap().len();
     }
 }
 
 #[derive(Default)]
-pub(crate) struct SyntaxTreeStats {
+pub(crate) struct SyntaxTreeStats<const MACROS: bool> {
     total: usize,
     pub(crate) retained: usize,
 }
 
-impl fmt::Display for SyntaxTreeStats {
+impl<const MACROS: bool> fmt::Display for SyntaxTreeStats<MACROS> {
     fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(fmt, "{} trees, {} preserved", self.total, self.retained)
+        write!(
+            fmt,
+            "{} trees, {} preserved{}",
+            self.total,
+            self.retained,
+            if MACROS { " (macros)" } else { "" }
+        )
     }
 }
 
-impl FromIterator<TableEntry<FileId, Parse<ast::SourceFile>>> for SyntaxTreeStats {
-    fn from_iter<T>(iter: T) -> SyntaxTreeStats
-    where
-        T: IntoIterator<Item = TableEntry<FileId, Parse<ast::SourceFile>>>,
-    {
-        let mut res = SyntaxTreeStats::default();
-        for entry in iter {
-            res.total += 1;
-            res.retained += entry.value.is_some() as usize;
-        }
-        res
+impl StatCollect<FileId, Parse<ast::SourceFile>> for SyntaxTreeStats<false> {
+    fn collect_entry(&mut self, _: FileId, value: Option<Parse<ast::SourceFile>>) {
+        self.total += 1;
+        self.retained += value.is_some() as usize;
     }
 }
 
-impl<M> FromIterator<TableEntry<MacroFile, ExpandResult<Option<(Parse<SyntaxNode>, M)>>>>
-    for SyntaxTreeStats
-{
-    fn from_iter<T>(iter: T) -> SyntaxTreeStats
-    where
-        T: IntoIterator<Item = TableEntry<MacroFile, ExpandResult<Option<(Parse<SyntaxNode>, M)>>>>,
-    {
-        let mut res = SyntaxTreeStats::default();
-        for entry in iter {
-            res.total += 1;
-            res.retained += entry.value.is_some() as usize;
+impl<M> StatCollect<MacroFile, ExpandResult<(Parse<SyntaxNode>, M)>> for SyntaxTreeStats<true> {
+    fn collect_entry(&mut self, _: MacroFile, value: Option<ExpandResult<(Parse<SyntaxNode>, M)>>) {
+        self.total += 1;
+        self.retained += value.is_some() as usize;
+    }
+}
+
+struct SymbolsStats<Key> {
+    total: usize,
+    size: Bytes,
+    phantom: PhantomData<Key>,
+}
+
+impl<Key> Default for SymbolsStats<Key> {
+    fn default() -> Self {
+        Self { total: Default::default(), size: Default::default(), phantom: PhantomData }
+    }
+}
+
+impl fmt::Display for SymbolsStats<Module> {
+    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(fmt, "{} of module index symbols ({})", self.size, self.total)
+    }
+}
+impl fmt::Display for SymbolsStats<SourceRootId> {
+    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(fmt, "{} of library index symbols ({})", self.size, self.total)
+    }
+}
+impl<Key> StatCollect<Key, Arc<SymbolIndex>> for SymbolsStats<Key> {
+    fn collect_entry(&mut self, _: Key, value: Option<Arc<SymbolIndex>>) {
+        if let Some(symbols) = value {
+            self.total += symbols.len();
+            self.size += symbols.memory_size();
         }
-        res
     }
 }
 
 #[derive(Default)]
-struct LibrarySymbolsStats {
+struct AttrsStats {
+    entries: usize,
     total: usize,
-    size: Bytes,
 }
 
-impl fmt::Display for LibrarySymbolsStats {
+impl fmt::Display for AttrsStats {
     fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(fmt, "{} of index symbols ({})", self.size, self.total)
+        let size =
+            self.entries * std::mem::size_of::<Attrs>() + self.total * std::mem::size_of::<Attr>();
+        let size = Bytes::new(size as _);
+        write!(
+            fmt,
+            "{} attribute query entries, {} total attributes ({} for storing entries)",
+            self.entries, self.total, size
+        )
     }
 }
 
-impl FromIterator<TableEntry<SourceRootId, Arc<SymbolIndex>>> for LibrarySymbolsStats {
-    fn from_iter<T>(iter: T) -> LibrarySymbolsStats
-    where
-        T: IntoIterator<Item = TableEntry<SourceRootId, Arc<SymbolIndex>>>,
-    {
-        let mut res = LibrarySymbolsStats::default();
-        for entry in iter {
-            let symbols = entry.value.unwrap();
-            res.total += symbols.len();
-            res.size += symbols.memory_size();
-        }
-        res
+impl<Key> StatCollect<Key, Attrs> for AttrsStats {
+    fn collect_entry(&mut self, _: Key, value: Option<Attrs>) {
+        self.entries += 1;
+        self.total += value.map_or(0, |it| it.len());
     }
 }
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs
index 454a250f3de..8c02fe81648 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs
@@ -16,13 +16,19 @@ mod tests;
 use hir::{Name, Semantics};
 use ide_db::{FxHashMap, RootDatabase, SymbolKind};
 use syntax::{
-    ast, AstNode, AstToken, NodeOrToken, SyntaxKind::*, SyntaxNode, TextRange, WalkEvent, T,
+    ast::{self, IsString},
+    AstNode, AstToken, NodeOrToken,
+    SyntaxKind::*,
+    SyntaxNode, TextRange, WalkEvent, T,
 };
 
 use crate::{
     syntax_highlighting::{
-        escape::highlight_escape_string, format::highlight_format_string, highlights::Highlights,
-        macro_::MacroHighlighter, tags::Highlight,
+        escape::{highlight_escape_char, highlight_escape_string},
+        format::highlight_format_string,
+        highlights::Highlights,
+        macro_::MacroHighlighter,
+        tags::Highlight,
     },
     FileId, HlMod, HlOperator, HlPunct, HlTag,
 };
@@ -163,6 +169,7 @@ pub struct HighlightConfig {
 // injected:: Emitted for doc-string injected highlighting like rust source blocks in documentation.
 // intraDocLink:: Emitted for intra doc links in doc-strings.
 // library:: Emitted for items that are defined outside of the current crate.
+// macro::  Emitted for tokens inside macro calls.
 // mutable:: Emitted for mutable locals and statics as well as functions taking `&mut self`.
 // public:: Emitted for items that are from the current crate and are `pub`.
 // reference:: Emitted for locals behind a reference and functions taking `self` by reference.
@@ -237,6 +244,7 @@ fn traverse(
     let mut current_macro: Option<ast::Macro> = None;
     let mut macro_highlighter = MacroHighlighter::default();
     let mut inside_attribute = false;
+    let mut inside_macro_call = false;
 
     // Walk all nodes, keeping track of whether we are inside a macro or not.
     // If in macro, expand it first and highlight the expanded code.
@@ -267,46 +275,50 @@ fn traverse(
                 inside_attribute = false
             }
 
-            Enter(NodeOrToken::Node(node)) if ast::Item::can_cast(node.kind()) => {
-                match ast::Item::cast(node.clone()) {
-                    Some(ast::Item::MacroRules(mac)) => {
-                        macro_highlighter.init();
-                        current_macro = Some(mac.into());
-                        continue;
-                    }
-                    Some(ast::Item::MacroDef(mac)) => {
-                        macro_highlighter.init();
-                        current_macro = Some(mac.into());
-                        continue;
-                    }
-                    Some(item) => {
-                        if matches!(node.kind(), FN | CONST | STATIC) {
-                            bindings_shadow_count.clear();
+            Enter(NodeOrToken::Node(node)) => match ast::Item::cast(node.clone()) {
+                Some(item) => {
+                    match item {
+                        ast::Item::MacroRules(mac) => {
+                            macro_highlighter.init();
+                            current_macro = Some(mac.into());
+                            continue;
+                        }
+                        ast::Item::MacroDef(mac) => {
+                            macro_highlighter.init();
+                            current_macro = Some(mac.into());
+                            continue;
+                        }
+                        ast::Item::Fn(_) | ast::Item::Const(_) | ast::Item::Static(_) => {
+                            bindings_shadow_count.clear()
+                        }
+                        ast::Item::MacroCall(_) => {
+                            inside_macro_call = true;
                         }
+                        _ => (),
+                    }
 
-                        if attr_or_derive_item.is_none() {
-                            if sema.is_attr_macro_call(&item) {
-                                attr_or_derive_item = Some(AttrOrDerive::Attr(item));
-                            } else {
-                                let adt = match item {
-                                    ast::Item::Enum(it) => Some(ast::Adt::Enum(it)),
-                                    ast::Item::Struct(it) => Some(ast::Adt::Struct(it)),
-                                    ast::Item::Union(it) => Some(ast::Adt::Union(it)),
-                                    _ => None,
-                                };
-                                match adt {
-                                    Some(adt) if sema.is_derive_annotated(&adt) => {
-                                        attr_or_derive_item =
-                                            Some(AttrOrDerive::Derive(ast::Item::from(adt)));
-                                    }
-                                    _ => (),
+                    if attr_or_derive_item.is_none() {
+                        if sema.is_attr_macro_call(&item) {
+                            attr_or_derive_item = Some(AttrOrDerive::Attr(item));
+                        } else {
+                            let adt = match item {
+                                ast::Item::Enum(it) => Some(ast::Adt::Enum(it)),
+                                ast::Item::Struct(it) => Some(ast::Adt::Struct(it)),
+                                ast::Item::Union(it) => Some(ast::Adt::Union(it)),
+                                _ => None,
+                            };
+                            match adt {
+                                Some(adt) if sema.is_derive_annotated(&adt) => {
+                                    attr_or_derive_item =
+                                        Some(AttrOrDerive::Derive(ast::Item::from(adt)));
                                 }
+                                _ => (),
                             }
                         }
                     }
-                    _ => (),
                 }
-            }
+                _ => (),
+            },
             Leave(NodeOrToken::Node(node)) if ast::Item::can_cast(node.kind()) => {
                 match ast::Item::cast(node.clone()) {
                     Some(ast::Item::MacroRules(mac)) => {
@@ -324,6 +336,9 @@ fn traverse(
                     {
                         attr_or_derive_item = None;
                     }
+                    Some(ast::Item::MacroCall(_)) => {
+                        inside_macro_call = false;
+                    }
                     _ => (),
                 }
             }
@@ -419,14 +434,35 @@ fn traverse(
                         continue;
                     }
                     highlight_format_string(hl, &string, &expanded_string, range);
-                    highlight_escape_string(hl, &string, range.start());
+
+                    if !string.is_raw() {
+                        highlight_escape_string(hl, &string, range.start());
+                    }
                 }
             } else if ast::ByteString::can_cast(token.kind())
                 && ast::ByteString::can_cast(descended_token.kind())
             {
                 if let Some(byte_string) = ast::ByteString::cast(token) {
-                    highlight_escape_string(hl, &byte_string, range.start());
+                    if !byte_string.is_raw() {
+                        highlight_escape_string(hl, &byte_string, range.start());
+                    }
                 }
+            } else if ast::CString::can_cast(token.kind())
+                && ast::CString::can_cast(descended_token.kind())
+            {
+                if let Some(c_string) = ast::CString::cast(token) {
+                    if !c_string.is_raw() {
+                        highlight_escape_string(hl, &c_string, range.start());
+                    }
+                }
+            } else if ast::Char::can_cast(token.kind())
+                && ast::Char::can_cast(descended_token.kind())
+            {
+                let Some(char) = ast::Char::cast(token) else {
+                    continue;
+                };
+
+                highlight_escape_char(hl, &char, range.start())
             }
         }
 
@@ -455,32 +491,42 @@ fn traverse(
             }
 
             // apply config filtering
-            match &mut highlight.tag {
-                HlTag::StringLiteral if !config.strings => continue,
-                // If punctuation is disabled, make the macro bang part of the macro call again.
-                tag @ HlTag::Punctuation(HlPunct::MacroBang) => {
-                    if !config.macro_bang {
-                        *tag = HlTag::Symbol(SymbolKind::Macro);
-                    } else if !config.specialize_punctuation {
-                        *tag = HlTag::Punctuation(HlPunct::Other);
-                    }
-                }
-                HlTag::Punctuation(_) if !config.punctuation => continue,
-                tag @ HlTag::Punctuation(_) if !config.specialize_punctuation => {
-                    *tag = HlTag::Punctuation(HlPunct::Other);
-                }
-                HlTag::Operator(_) if !config.operator && highlight.mods.is_empty() => continue,
-                tag @ HlTag::Operator(_) if !config.specialize_operator => {
-                    *tag = HlTag::Operator(HlOperator::Other);
-                }
-                _ => (),
+            if !filter_by_config(&mut highlight, config) {
+                continue;
             }
 
             if inside_attribute {
                 highlight |= HlMod::Attribute
             }
+            if inside_macro_call && tt_level > 0 {
+                highlight |= HlMod::Macro
+            }
 
             hl.add(HlRange { range, highlight, binding_hash });
         }
     }
 }
+
+fn filter_by_config(highlight: &mut Highlight, config: HighlightConfig) -> bool {
+    match &mut highlight.tag {
+        HlTag::StringLiteral if !config.strings => return false,
+        // If punctuation is disabled, make the macro bang part of the macro call again.
+        tag @ HlTag::Punctuation(HlPunct::MacroBang) => {
+            if !config.macro_bang {
+                *tag = HlTag::Symbol(SymbolKind::Macro);
+            } else if !config.specialize_punctuation {
+                *tag = HlTag::Punctuation(HlPunct::Other);
+            }
+        }
+        HlTag::Punctuation(_) if !config.punctuation => return false,
+        tag @ HlTag::Punctuation(_) if !config.specialize_punctuation => {
+            *tag = HlTag::Punctuation(HlPunct::Other);
+        }
+        HlTag::Operator(_) if !config.operator && highlight.mods.is_empty() => return false,
+        tag @ HlTag::Operator(_) if !config.specialize_operator => {
+            *tag = HlTag::Operator(HlOperator::Other);
+        }
+        _ => (),
+    }
+    true
+}
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/escape.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/escape.rs
index 6a1236c793b..211e3588095 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/escape.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/escape.rs
@@ -1,8 +1,8 @@
 //! Syntax highlighting for escape sequences
 use crate::syntax_highlighting::highlights::Highlights;
 use crate::{HlRange, HlTag};
-use syntax::ast::IsString;
-use syntax::TextSize;
+use syntax::ast::{Char, IsString};
+use syntax::{AstToken, TextRange, TextSize};
 
 pub(super) fn highlight_escape_string<T: IsString>(
     stack: &mut Highlights,
@@ -23,3 +23,23 @@ pub(super) fn highlight_escape_string<T: IsString>(
         }
     });
 }
+
+pub(super) fn highlight_escape_char(stack: &mut Highlights, char: &Char, start: TextSize) {
+    if char.value().is_none() {
+        return;
+    }
+
+    let text = char.text();
+    if !text.starts_with('\'') || !text.ends_with('\'') {
+        return;
+    }
+
+    let text = &text[1..text.len() - 1];
+    if !text.starts_with('\\') {
+        return;
+    }
+
+    let range =
+        TextRange::new(start + TextSize::from(1), start + TextSize::from(text.len() as u32 + 1));
+    stack.add(HlRange { range, highlight: HlTag::EscapeSequence.into(), binding_hash: None })
+}
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs
index 2111baad74d..3c40246a69d 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs
@@ -26,7 +26,7 @@ pub(super) fn token(sema: &Semantics<'_, RootDatabase>, token: SyntaxToken) -> O
     }
 
     let highlight: Highlight = match token.kind() {
-        STRING | BYTE_STRING => HlTag::StringLiteral.into(),
+        STRING | BYTE_STRING | C_STRING => HlTag::StringLiteral.into(),
         INT_NUMBER if token.parent_ancestors().nth(1).map(|it| it.kind()) == Some(FIELD_EXPR) => {
             SymbolKind::Field.into()
         }
@@ -340,7 +340,7 @@ fn highlight_def(
         Definition::Field(_) => Highlight::new(HlTag::Symbol(SymbolKind::Field)),
         Definition::Module(module) => {
             let mut h = Highlight::new(HlTag::Symbol(SymbolKind::Module));
-            if module.is_crate_root(db) {
+            if module.is_crate_root() {
                 h |= HlMod::CrateRoot;
             }
             h
@@ -675,14 +675,12 @@ fn is_consumed_lvalue(node: &SyntaxNode, local: &hir::Local, db: &RootDatabase)
 
 /// Returns true if the parent nodes of `node` all match the `SyntaxKind`s in `kinds` exactly.
 fn parents_match(mut node: NodeOrToken<SyntaxNode, SyntaxToken>, mut kinds: &[SyntaxKind]) -> bool {
-    while let (Some(parent), [kind, rest @ ..]) = (&node.parent(), kinds) {
+    while let (Some(parent), [kind, rest @ ..]) = (node.parent(), kinds) {
         if parent.kind() != *kind {
             return false;
         }
 
-        // FIXME: Would be nice to get parent out of the match, but binding by-move and by-value
-        // in the same pattern is unstable: rust-lang/rust#68354.
-        node = node.parent().unwrap().into();
+        node = parent.into();
         kinds = rest;
     }
 
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs
index 3c4cfc78152..901df147d32 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs
@@ -52,7 +52,11 @@ pub(super) fn ra_fixture(
 
         if let Some(next) = text.strip_prefix(marker) {
             if let Some(range) = literal.map_range_up(TextRange::at(offset, TextSize::of(marker))) {
-                hl.add(HlRange { range, highlight: HlTag::Keyword.into(), binding_hash: None });
+                hl.add(HlRange {
+                    range,
+                    highlight: HlTag::Keyword | HlMod::Injected,
+                    binding_hash: None,
+                });
             }
 
             text = next;
@@ -66,7 +70,16 @@ pub(super) fn ra_fixture(
 
     for mut hl_range in analysis
         .highlight(
-            HighlightConfig { syntactic_name_ref_highlighting: false, ..config },
+            HighlightConfig {
+                syntactic_name_ref_highlighting: false,
+                punctuation: true,
+                operator: true,
+                strings: true,
+                specialize_punctuation: config.specialize_punctuation,
+                specialize_operator: config.operator,
+                inject_doc_comment: config.inject_doc_comment,
+                macro_bang: config.macro_bang,
+            },
             tmp_file_id,
         )
         .unwrap()
@@ -74,6 +87,7 @@ pub(super) fn ra_fixture(
         for range in inj.map_range_up(hl_range.range) {
             if let Some(range) = literal.map_range_up(range) {
                 hl_range.range = range;
+                hl_range.highlight |= HlMod::Injected;
                 hl.add(hl_range);
             }
         }
@@ -217,7 +231,16 @@ pub(super) fn doc_comment(
     if let Ok(ranges) = analysis.with_db(|db| {
         super::highlight(
             db,
-            HighlightConfig { syntactic_name_ref_highlighting: true, ..config },
+            HighlightConfig {
+                syntactic_name_ref_highlighting: true,
+                punctuation: true,
+                operator: true,
+                strings: true,
+                specialize_punctuation: config.specialize_punctuation,
+                specialize_operator: config.operator,
+                inject_doc_comment: config.inject_doc_comment,
+                macro_bang: config.macro_bang,
+            },
             tmp_file_id,
             None,
         )
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs
index a81c4ee0cbd..f983109115f 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs
@@ -49,7 +49,7 @@ pub enum HlMod {
     Associated = 0,
     /// Used with keywords like `async` and `await`.
     Async,
-    /// Used to differentiate individual elements within attributes.
+    /// Used to differentiate individual elements within attribute calls.
     Attribute,
     /// Callable item or value.
     Callable,
@@ -72,6 +72,8 @@ pub enum HlMod {
     IntraDocLink,
     /// Used for items from other crates.
     Library,
+    /// Used to differentiate individual elements within macro calls.
+    Macro,
     /// Mutable binding.
     Mutable,
     /// Used for public items.
@@ -200,7 +202,7 @@ impl fmt::Display for HlTag {
 }
 
 impl HlMod {
-    const ALL: &'static [HlMod; 19] = &[
+    const ALL: &'static [HlMod; HlMod::Unsafe as usize + 1] = &[
         HlMod::Associated,
         HlMod::Async,
         HlMod::Attribute,
@@ -214,6 +216,7 @@ impl HlMod {
         HlMod::Injected,
         HlMod::IntraDocLink,
         HlMod::Library,
+        HlMod::Macro,
         HlMod::Mutable,
         HlMod::Public,
         HlMod::Reference,
@@ -237,6 +240,7 @@ impl HlMod {
             HlMod::Injected => "injected",
             HlMod::IntraDocLink => "intra_doc_link",
             HlMod::Library => "library",
+            HlMod::Macro => "macro",
             HlMod::Mutable => "mutable",
             HlMod::Public => "public",
             HlMod::Reference => "reference",
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
index 18045f1f55a..35f240d4284 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
@@ -93,7 +93,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     <span class="comment documentation">///</span><span class="comment documentation"> </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="variable declaration injected">foo</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="struct injected">Foo</span><span class="operator injected">::</span><span class="function injected">new</span><span class="parenthesis injected">(</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span>
     <span class="comment documentation">///</span>
     <span class="comment documentation">///</span><span class="comment documentation"> </span><span class="comment injected">// calls bar on foo</span>
-    <span class="comment documentation">///</span><span class="comment documentation"> </span><span class="macro injected">assert</span><span class="macro_bang injected">!</span><span class="parenthesis injected">(</span><span class="none injected">foo</span><span class="operator injected">.</span><span class="none injected">bar</span><span class="parenthesis injected">(</span><span class="parenthesis injected">)</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span>
+    <span class="comment documentation">///</span><span class="comment documentation"> </span><span class="macro injected">assert</span><span class="macro_bang injected">!</span><span class="parenthesis injected macro">(</span><span class="none injected macro">foo</span><span class="operator injected macro">.</span><span class="none injected macro">bar</span><span class="parenthesis injected macro">(</span><span class="parenthesis injected macro">)</span><span class="parenthesis injected macro">)</span><span class="semicolon injected">;</span>
     <span class="comment documentation">///</span>
     <span class="comment documentation">///</span><span class="comment documentation"> </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="variable declaration injected">bar</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="variable injected">foo</span><span class="operator injected">.</span><span class="field injected">bar</span><span class="none injected"> </span><span class="logical injected">||</span><span class="none injected"> </span><span class="struct injected">Foo</span><span class="operator injected">::</span><span class="constant injected">bar</span><span class="semicolon injected">;</span>
     <span class="comment documentation">///</span>
@@ -145,7 +145,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 
 <span class="comment documentation">/// ```</span>
 <span class="comment documentation">///</span><span class="comment documentation"> </span><span class="keyword injected">macro_rules</span><span class="macro_bang injected">!</span><span class="none injected"> </span><span class="macro declaration injected">noop</span><span class="none injected"> </span><span class="brace injected">{</span><span class="none injected"> </span><span class="parenthesis injected">(</span><span class="punctuation injected">$</span><span class="none injected">expr</span><span class="colon injected">:</span><span class="none injected">expr</span><span class="parenthesis injected">)</span><span class="none injected"> </span><span class="operator injected">=</span><span class="angle injected">&gt;</span><span class="none injected"> </span><span class="brace injected">{</span><span class="none injected"> </span><span class="punctuation injected">$</span><span class="none injected">expr </span><span class="brace injected">}</span><span class="brace injected">}</span>
-<span class="comment documentation">///</span><span class="comment documentation"> </span><span class="macro injected">noop</span><span class="macro_bang injected">!</span><span class="parenthesis injected">(</span><span class="numeric_literal injected">1</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span>
+<span class="comment documentation">///</span><span class="comment documentation"> </span><span class="macro injected">noop</span><span class="macro_bang injected">!</span><span class="parenthesis injected macro">(</span><span class="numeric_literal injected macro">1</span><span class="parenthesis injected macro">)</span><span class="semicolon injected">;</span>
 <span class="comment documentation">/// ```</span>
 <span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">noop</span> <span class="brace">{</span>
     <span class="parenthesis">(</span><span class="punctuation">$</span>expr<span class="colon">:</span>expr<span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="brace">{</span>
@@ -165,7 +165,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 <span class="comment documentation">///</span>
 <span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute library">cfg_attr</span><span class="parenthesis attribute">(</span><span class="none attribute">feature</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"alloc"</span><span class="comma attribute">,</span> <span class="none attribute">doc</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"```rust"</span><span class="parenthesis attribute">)</span><span class="attribute_bracket attribute">]</span>
 <span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute library">cfg_attr</span><span class="parenthesis attribute">(</span><span class="none attribute">not</span><span class="parenthesis attribute">(</span><span class="none attribute">feature</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"alloc"</span><span class="parenthesis attribute">)</span><span class="comma attribute">,</span> <span class="none attribute">doc</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"```ignore"</span><span class="parenthesis attribute">)</span><span class="attribute_bracket attribute">]</span>
-<span class="comment documentation">///</span><span class="comment documentation"> </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="punctuation injected">_</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="function injected">example</span><span class="parenthesis injected">(</span><span class="operator injected">&</span><span class="module injected">alloc</span><span class="operator injected">::</span><span class="macro injected">vec</span><span class="macro_bang injected">!</span><span class="bracket injected">[</span><span class="numeric_literal injected">1</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">2</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">3</span><span class="bracket injected">]</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span>
+<span class="comment documentation">///</span><span class="comment documentation"> </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="punctuation injected">_</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="function injected">example</span><span class="parenthesis injected">(</span><span class="operator injected">&</span><span class="module injected">alloc</span><span class="operator injected">::</span><span class="macro injected">vec</span><span class="macro_bang injected">!</span><span class="bracket injected macro">[</span><span class="numeric_literal injected macro">1</span><span class="comma injected macro">,</span><span class="none injected"> </span><span class="numeric_literal injected macro">2</span><span class="comma injected macro">,</span><span class="none injected"> </span><span class="numeric_literal injected macro">3</span><span class="bracket injected macro">]</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span>
 <span class="comment documentation">/// ```</span>
 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration public">mix_and_match</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
 
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html
index af41796e216..87b9da46e2c 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html
@@ -43,5 +43,5 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
 </style>
 <pre><code><span class="keyword">extern</span> <span class="keyword">crate</span> <span class="module crate_root default_library library">std</span><span class="semicolon">;</span>
-<span class="keyword">extern</span> <span class="keyword">crate</span> <span class="module crate_root library">alloc</span> <span class="keyword">as</span> <span class="module crate_root declaration library">abc</span><span class="semicolon">;</span>
+<span class="keyword">extern</span> <span class="keyword">crate</span> <span class="module crate_root default_library library">alloc</span> <span class="keyword">as</span> <span class="module crate_root default_library declaration library">abc</span><span class="semicolon">;</span>
 </code></pre>
\ No newline at end of file
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html
index 9f2b1926b51..6b049f379ac 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html
@@ -178,7 +178,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 <span class="keyword">impl</span><span class="angle">&lt;</span><span class="type_param declaration">T</span><span class="angle">&gt;</span> <span class="enum">Option</span><span class="angle">&lt;</span><span class="type_param">T</span><span class="angle">&gt;</span> <span class="brace">{</span>
     <span class="keyword">fn</span> <span class="function associated consuming declaration">and</span><span class="angle">&lt;</span><span class="type_param declaration">U</span><span class="angle">&gt;</span><span class="parenthesis">(</span><span class="self_keyword declaration">self</span><span class="comma">,</span> <span class="value_param declaration">other</span><span class="colon">:</span> <span class="enum">Option</span><span class="angle">&lt;</span><span class="type_param">U</span><span class="angle">&gt;</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="enum">Option</span><span class="angle">&lt;</span><span class="parenthesis">(</span><span class="type_param">T</span><span class="comma">,</span> <span class="type_param">U</span><span class="parenthesis">)</span><span class="angle">&gt;</span> <span class="brace">{</span>
         <span class="keyword control">match</span> <span class="value_param">other</span> <span class="brace">{</span>
-            <span class="enum_variant">None</span> <span class="operator">=&gt;</span> <span class="unresolved_reference">unimplemented</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="comma">,</span>
+            <span class="enum_variant">None</span> <span class="operator">=&gt;</span> <span class="unresolved_reference">unimplemented</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="parenthesis macro">)</span><span class="comma">,</span>
             <span class="variable declaration">Nope</span> <span class="operator">=&gt;</span> <span class="variable">Nope</span><span class="comma">,</span>
         <span class="brace">}</span>
     <span class="brace">}</span>
@@ -192,7 +192,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 <span class="keyword async">async</span> <span class="keyword">fn</span> <span class="function async declaration">async_main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
     <span class="keyword">let</span> <span class="variable declaration">f1</span> <span class="operator">=</span> <span class="function async">learn_and_sing</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
     <span class="keyword">let</span> <span class="variable declaration">f2</span> <span class="operator">=</span> <span class="unresolved_reference">dance</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="unresolved_reference">futures</span><span class="operator">::</span><span class="unresolved_reference">join</span><span class="macro_bang">!</span><span class="parenthesis">(</span>f1<span class="comma">,</span> f2<span class="parenthesis">)</span><span class="semicolon">;</span>
+    <span class="unresolved_reference">futures</span><span class="operator">::</span><span class="unresolved_reference">join</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="none macro">f1</span><span class="comma macro">,</span> <span class="none macro">f2</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
 <span class="brace">}</span>
 
 <span class="keyword">fn</span> <span class="function declaration">use_foo_items</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
@@ -204,7 +204,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     <span class="keyword">let</span> <span class="variable declaration">control_flow</span> <span class="operator">=</span> <span class="module crate_root library">foo</span><span class="operator">::</span><span class="function library">identity</span><span class="parenthesis">(</span><span class="module crate_root library">foo</span><span class="operator">::</span><span class="enum library">ControlFlow</span><span class="operator">::</span><span class="enum_variant library">Continue</span><span class="parenthesis">)</span><span class="semicolon">;</span>
 
     <span class="keyword control">if</span> <span class="variable">control_flow</span><span class="operator">.</span><span class="function associated consuming library">should_die</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
-        <span class="module crate_root library">foo</span><span class="operator">::</span><span class="unresolved_reference">die</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+        <span class="module crate_root library">foo</span><span class="operator">::</span><span class="unresolved_reference">die</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
     <span class="brace">}</span>
 <span class="brace">}</span>
 
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html
index abcd80c280b..d9c3db6fbb5 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html
@@ -45,18 +45,18 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 <pre><code><span class="keyword">fn</span> <span class="function declaration">fixture</span><span class="parenthesis">(</span><span class="value_param declaration reference">ra_fixture</span><span class="colon">:</span> <span class="punctuation">&</span><span class="builtin_type">str</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
 
 <span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
-    <span class="function">fixture</span><span class="parenthesis">(</span><span class="string_literal">r#"</span>
-<span class="keyword">trait</span> <span class="trait declaration">Foo</span> <span class="brace">{</span>
-    <span class="keyword">fn</span> <span class="function associated declaration static trait">foo</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
-        <span class="unresolved_reference">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"2 + 2 = {}"</span><span class="comma">,</span> <span class="numeric_literal">4</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="brace">}</span>
-<span class="brace">}</span><span class="string_literal">"#</span>
+    <span class="function">fixture</span><span class="parenthesis">(</span><span class="string_literal">r#"</span><span class="none injected">
+</span><span class="keyword injected">trait</span><span class="none injected"> </span><span class="trait declaration injected">Foo</span><span class="none injected"> </span><span class="brace injected">{</span><span class="none injected">
+    </span><span class="keyword injected">fn</span><span class="none injected"> </span><span class="function associated declaration injected static trait">foo</span><span class="parenthesis injected">(</span><span class="parenthesis injected">)</span><span class="none injected"> </span><span class="brace injected">{</span><span class="none injected">
+        </span><span class="unresolved_reference injected">println</span><span class="macro_bang injected">!</span><span class="parenthesis injected macro">(</span><span class="string_literal injected macro">"2 + 2 = {}"</span><span class="comma injected macro">,</span><span class="none injected"> </span><span class="numeric_literal injected macro">4</span><span class="parenthesis injected macro">)</span><span class="semicolon injected">;</span><span class="none injected">
+    </span><span class="brace injected">}</span><span class="none injected">
+</span><span class="brace injected">}</span><span class="string_literal">"#</span>
     <span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="function">fixture</span><span class="parenthesis">(</span><span class="string_literal">r"</span>
-<span class="keyword">fn</span> <span class="function declaration">foo</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
-    <span class="function">foo</span><span class="parenthesis">(</span><span class="keyword">$0</span><span class="brace">{</span>
-        <span class="numeric_literal">92</span>
-    <span class="brace">}</span><span class="keyword">$0</span><span class="parenthesis">)</span>
-<span class="brace">}</span><span class="string_literal">"</span>
+    <span class="function">fixture</span><span class="parenthesis">(</span><span class="string_literal">r"</span><span class="none injected">
+</span><span class="keyword injected">fn</span><span class="none injected"> </span><span class="function declaration injected">foo</span><span class="parenthesis injected">(</span><span class="parenthesis injected">)</span><span class="none injected"> </span><span class="brace injected">{</span><span class="none injected">
+    </span><span class="function injected">foo</span><span class="parenthesis injected">(</span><span class="keyword injected">$0</span><span class="brace injected">{</span><span class="none injected">
+        </span><span class="numeric_literal injected">92</span><span class="none injected">
+    </span><span class="brace injected">}</span><span class="keyword injected">$0</span><span class="parenthesis injected">)</span><span class="none injected">
+</span><span class="brace injected">}</span><span class="string_literal">"</span>
     <span class="parenthesis">)</span><span class="semicolon">;</span>
 <span class="brace">}</span></code></pre>
\ No newline at end of file
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords.html
index 66f9ede9629..3900959bedf 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords.html
@@ -53,6 +53,6 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 <span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">void</span> <span class="brace">{</span>
     <span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="brace">{</span><span class="brace">}</span>
 <span class="brace">}</span>
-<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="keyword">Self</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="keyword macro">Self</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
 <span class="keyword">struct</span> <span class="struct declaration">__</span> <span class="keyword">where</span> <span class="self_type_keyword">Self</span><span class="colon">:</span><span class="semicolon">;</span>
 <span class="keyword">fn</span> <span class="function declaration">__</span><span class="parenthesis">(</span><span class="punctuation">_</span><span class="colon">:</span> <span class="unresolved_reference">Self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span></code></pre>
\ No newline at end of file
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html
index 54d4279525d..2cbbf696415 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html
@@ -42,21 +42,21 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 
 .unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
 </style>
-<pre><code><span class="module crate_root library">proc_macros</span><span class="operator">::</span><span class="macro library">mirror</span><span class="macro_bang">!</span> <span class="brace">{</span>
-    <span class="brace">{</span>
-        <span class="comma">,</span><span class="builtin_type">i32</span> <span class="colon">:</span><span class="field declaration public">x</span> <span class="keyword">pub</span>
-        <span class="comma">,</span><span class="builtin_type">i32</span> <span class="colon">:</span><span class="field declaration public">y</span> <span class="keyword">pub</span>
-    <span class="brace">}</span> <span class="struct declaration">Foo</span> <span class="keyword">struct</span>
-<span class="brace">}</span>
+<pre><code><span class="module crate_root library">proc_macros</span><span class="operator">::</span><span class="macro library">mirror</span><span class="macro_bang">!</span> <span class="brace macro">{</span>
+    <span class="brace macro">{</span>
+        <span class="comma macro">,</span><span class="builtin_type macro">i32</span> <span class="colon macro">:</span><span class="field declaration macro public">x</span> <span class="keyword macro">pub</span>
+        <span class="comma macro">,</span><span class="builtin_type macro">i32</span> <span class="colon macro">:</span><span class="field declaration macro public">y</span> <span class="keyword macro">pub</span>
+    <span class="brace macro">}</span> <span class="struct declaration macro">Foo</span> <span class="keyword macro">struct</span>
+<span class="brace macro">}</span>
 <span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">def_fn</span> <span class="brace">{</span>
     <span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="brace">{</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="brace">}</span>
 <span class="brace">}</span>
 
-<span class="macro">def_fn</span><span class="macro_bang">!</span> <span class="brace">{</span>
-    <span class="keyword">fn</span> <span class="function declaration">bar</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">-</span><span class="operator">&gt;</span> <span class="builtin_type">u32</span> <span class="brace">{</span>
-        <span class="numeric_literal">100</span>
-    <span class="brace">}</span>
-<span class="brace">}</span>
+<span class="macro">def_fn</span><span class="macro_bang">!</span> <span class="brace macro">{</span>
+    <span class="keyword macro">fn</span> <span class="function declaration macro">bar</span><span class="parenthesis macro">(</span><span class="parenthesis macro">)</span> <span class="operator macro">-</span><span class="operator macro">&gt;</span> <span class="builtin_type macro">u32</span> <span class="brace macro">{</span>
+        <span class="numeric_literal macro">100</span>
+    <span class="brace macro">}</span>
+<span class="brace macro">}</span>
 
 <span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">dont_color_me_braces</span> <span class="brace">{</span>
     <span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="brace">{</span><span class="numeric_literal">0</span><span class="brace">}</span>
@@ -90,7 +90,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 <span class="brace">}</span>
 
 <span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
-    <span class="unresolved_reference">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello, {}!"</span><span class="comma">,</span> <span class="numeric_literal">92</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro">dont_color_me_braces</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro">noop</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="macro">noop</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="numeric_literal">1</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+    <span class="unresolved_reference">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello, {}!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">92</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro">dont_color_me_braces</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro">noop</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="macro macro">noop</span><span class="macro_bang macro">!</span><span class="parenthesis macro">(</span><span class="numeric_literal macro">1</span><span class="parenthesis macro">)</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
 <span class="brace">}</span></code></pre>
\ No newline at end of file
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
index a626cda3fe8..327e1502d19 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
@@ -93,72 +93,83 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 <span class="brace">}</span>
 
 <span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="escape_sequence">{{</span><span class="string_literal">Hello</span><span class="escape_sequence">}}</span><span class="string_literal">"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+    <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="char_literal">'</span><span class="escape_sequence">\n</span><span class="char_literal">'</span><span class="semicolon">;</span>
+    <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="char_literal">'</span><span class="escape_sequence">\t</span><span class="char_literal">'</span><span class="semicolon">;</span>
+    <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="char_literal">'\e'</span><span class="semicolon">;</span> <span class="comment">// invalid escape</span>
+    <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="char_literal">'e'</span><span class="semicolon">;</span>
+    <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="char_literal">' '</span><span class="semicolon">;</span>
+    <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="char_literal">'</span><span class="escape_sequence">\u{48}</span><span class="char_literal">'</span><span class="semicolon">;</span>
+    <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="char_literal">'</span><span class="escape_sequence">\u{4823}</span><span class="char_literal">'</span><span class="semicolon">;</span>
+    <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="char_literal">'</span><span class="escape_sequence">\x65</span><span class="char_literal">'</span><span class="semicolon">;</span>
+    <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="char_literal">'</span><span class="escape_sequence">\x00</span><span class="char_literal">'</span><span class="semicolon">;</span>
+
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="escape_sequence">{{</span><span class="string_literal macro">Hello</span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
     <span class="comment">// from https://doc.rust-lang.org/std/fmt/index.html</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello"</span><span class="parenthesis">)</span><span class="semicolon">;</span>                 <span class="comment">// =&gt; "Hello"</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello, </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="comma">,</span> <span class="string_literal">"world"</span><span class="parenthesis">)</span><span class="semicolon">;</span>   <span class="comment">// =&gt; "Hello, world!"</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"The number is </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="numeric_literal">1</span><span class="parenthesis">)</span><span class="semicolon">;</span>   <span class="comment">// =&gt; "The number is 1"</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">?</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="parenthesis">(</span><span class="numeric_literal">3</span><span class="comma">,</span> <span class="numeric_literal">4</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="semicolon">;</span>          <span class="comment">// =&gt; "(3, 4)"</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">value</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> value<span class="operator">=</span><span class="numeric_literal">4</span><span class="parenthesis">)</span><span class="semicolon">;</span>      <span class="comment">// =&gt; "4"</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="numeric_literal">1</span><span class="comma">,</span> <span class="numeric_literal">2</span><span class="parenthesis">)</span><span class="semicolon">;</span>           <span class="comment">// =&gt; "1 2"</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">4</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="numeric_literal">42</span><span class="parenthesis">)</span><span class="semicolon">;</span>             <span class="comment">// =&gt; "0042" with leading zerosV</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="numeric_literal">1</span><span class="comma">,</span> <span class="numeric_literal">2</span><span class="parenthesis">)</span><span class="semicolon">;</span>   <span class="comment">// =&gt; "2 1 1 2"</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">argument</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> argument <span class="operator">=</span> <span class="string_literal">"test"</span><span class="parenthesis">)</span><span class="semicolon">;</span>   <span class="comment">// =&gt; "test"</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="numeric_literal">1</span><span class="comma">,</span> name <span class="operator">=</span> <span class="numeric_literal">2</span><span class="parenthesis">)</span><span class="semicolon">;</span>          <span class="comment">// =&gt; "2 1"</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">a</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="variable">c</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="variable">b</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> a<span class="operator">=</span><span class="string_literal">"a"</span><span class="comma">,</span> b<span class="operator">=</span><span class="char_literal">'b'</span><span class="comma">,</span> c<span class="operator">=</span><span class="numeric_literal">3</span><span class="parenthesis">)</span><span class="semicolon">;</span>  <span class="comment">// =&gt; "a 3 b"</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="escape_sequence">{{</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="escape_sequence">}}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="numeric_literal">2</span><span class="parenthesis">)</span><span class="semicolon">;</span>                       <span class="comment">// =&gt; "{2}"</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">1</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="comma">,</span> <span class="numeric_literal">5</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="comma">,</span> <span class="numeric_literal">5</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="variable">width</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="comma">,</span> width <span class="operator">=</span> <span class="numeric_literal">5</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">&lt;</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">-</span><span class="format_specifier">&lt;</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">^</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">&gt;</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">+</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="comma">,</span> <span class="numeric_literal">5</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">#</span><span class="variable">x</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="comma">,</span> <span class="numeric_literal">27</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="comma">,</span> <span class="numeric_literal">5</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="comma">,</span> <span class="numeric_literal">-</span><span class="numeric_literal">5</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">#</span><span class="numeric_literal">0</span><span class="numeric_literal">10</span><span class="variable">x</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="comma">,</span> <span class="numeric_literal">27</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="comma">,</span> <span class="numeric_literal">0.01</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">0</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="numeric_literal">5</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="comma">,</span> <span class="numeric_literal">0.01</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">1</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="comma">,</span> <span class="numeric_literal">5</span><span class="comma">,</span> <span class="numeric_literal">0.01</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span>    <span class="string_literal">"x"</span><span class="comma">,</span> <span class="numeric_literal">5</span><span class="comma">,</span> <span class="numeric_literal">0.01</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span>   <span class="string_literal">"x"</span><span class="comma">,</span> <span class="numeric_literal">5</span><span class="comma">,</span> <span class="numeric_literal">0.01</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="variable">number</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="variable">prec</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="comma">,</span> prec <span class="operator">=</span> <span class="numeric_literal">5</span><span class="comma">,</span> number <span class="operator">=</span> <span class="numeric_literal">0.01</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal">` has 3 fractional digits"</span><span class="comma">,</span> <span class="string_literal">"Hello"</span><span class="comma">,</span> <span class="numeric_literal">3</span><span class="comma">,</span> name<span class="operator">=</span><span class="numeric_literal">1234.56</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal">` has 3 characters"</span><span class="comma">,</span> <span class="string_literal">"Hello"</span><span class="comma">,</span> <span class="numeric_literal">3</span><span class="comma">,</span> name<span class="operator">=</span><span class="string_literal">"1234.56"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">&gt;</span><span class="numeric_literal">8</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal">` has 3 right-aligned characters"</span><span class="comma">,</span> <span class="string_literal">"Hello"</span><span class="comma">,</span> <span class="numeric_literal">3</span><span class="comma">,</span> name<span class="operator">=</span><span class="string_literal">"1234.56"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>                 <span class="comment">// =&gt; "Hello"</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello, </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"world"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>   <span class="comment">// =&gt; "Hello, world!"</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"The number is </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>   <span class="comment">// =&gt; "The number is 1"</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">?</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="parenthesis macro">(</span><span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="numeric_literal macro">4</span><span class="parenthesis macro">)</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>          <span class="comment">// =&gt; "(3, 4)"</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">value</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="none macro">value</span><span class="operator macro">=</span><span class="numeric_literal macro">4</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>      <span class="comment">// =&gt; "4"</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="comma macro">,</span> <span class="numeric_literal macro">2</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>           <span class="comment">// =&gt; "1 2"</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">4</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">42</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>             <span class="comment">// =&gt; "0042" with leading zerosV</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="comma macro">,</span> <span class="numeric_literal macro">2</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>   <span class="comment">// =&gt; "2 1 1 2"</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">argument</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="none macro">argument</span> <span class="operator macro">=</span> <span class="string_literal macro">"test"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>   <span class="comment">// =&gt; "test"</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="comma macro">,</span> <span class="none macro">name</span> <span class="operator macro">=</span> <span class="numeric_literal macro">2</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>          <span class="comment">// =&gt; "2 1"</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">a</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="variable">c</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="variable">b</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="none macro">a</span><span class="operator macro">=</span><span class="string_literal macro">"a"</span><span class="comma macro">,</span> <span class="none macro">b</span><span class="operator macro">=</span><span class="char_literal macro">'b'</span><span class="comma macro">,</span> <span class="none macro">c</span><span class="operator macro">=</span><span class="numeric_literal macro">3</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>  <span class="comment">// =&gt; "a 3 b"</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="escape_sequence">{{</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">2</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>                       <span class="comment">// =&gt; "{2}"</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">1</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="variable">width</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="none macro">width</span> <span class="operator macro">=</span> <span class="numeric_literal macro">5</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">&lt;</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">-</span><span class="format_specifier">&lt;</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">^</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">&gt;</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">+</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">#</span><span class="variable">x</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">27</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">-</span><span class="numeric_literal macro">5</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">#</span><span class="numeric_literal">0</span><span class="numeric_literal">10</span><span class="variable">x</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">27</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">0</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">1</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span>    <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span>   <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="variable">number</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="variable">prec</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="none macro">prec</span> <span class="operator macro">=</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="none macro">number</span> <span class="operator macro">=</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">` has 3 fractional digits"</span><span class="comma macro">,</span> <span class="string_literal macro">"Hello"</span><span class="comma macro">,</span> <span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="none macro">name</span><span class="operator macro">=</span><span class="numeric_literal macro">1234.56</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">` has 3 characters"</span><span class="comma macro">,</span> <span class="string_literal macro">"Hello"</span><span class="comma macro">,</span> <span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="none macro">name</span><span class="operator macro">=</span><span class="string_literal macro">"1234.56"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">&gt;</span><span class="numeric_literal">8</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">` has 3 right-aligned characters"</span><span class="comma macro">,</span> <span class="string_literal macro">"Hello"</span><span class="comma macro">,</span> <span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="none macro">name</span><span class="operator macro">=</span><span class="string_literal macro">"1234.56"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
 
     <span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="string_literal">"{}"</span>
     <span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="string_literal">"{{}}"</span><span class="semicolon">;</span>
 
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="escape_sequence">{{</span><span class="escape_sequence">}}</span><span class="string_literal">"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="escape_sequence">{{</span><span class="string_literal"> Hello"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="escape_sequence">}}</span><span class="string_literal">"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="escape_sequence">{{</span><span class="string_literal">Hello</span><span class="escape_sequence">}}</span><span class="string_literal">"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="escape_sequence">{{</span><span class="string_literal"> Hello </span><span class="escape_sequence">}}</span><span class="string_literal">"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="escape_sequence">{{</span><span class="string_literal">Hello </span><span class="escape_sequence">}}</span><span class="string_literal">"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="escape_sequence">{{</span><span class="string_literal"> Hello</span><span class="escape_sequence">}}</span><span class="string_literal">"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="escape_sequence">{{</span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="escape_sequence">{{</span><span class="string_literal macro"> Hello"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="escape_sequence">{{</span><span class="string_literal macro">Hello</span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="escape_sequence">{{</span><span class="string_literal macro"> Hello </span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="escape_sequence">{{</span><span class="string_literal macro">Hello </span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="escape_sequence">{{</span><span class="string_literal macro"> Hello</span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
 
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">r"Hello, </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="comma">,</span> <span class="string_literal">"world"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">r"Hello, </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"world"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
 
     <span class="comment">// escape sequences</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello</span><span class="escape_sequence">\n</span><span class="string_literal">World"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="escape_sequence">\u{48}</span><span class="escape_sequence">\x65</span><span class="escape_sequence">\x6C</span><span class="escape_sequence">\x6C</span><span class="escape_sequence">\x6F</span><span class="string_literal"> World"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello</span><span class="escape_sequence">\n</span><span class="string_literal macro">World"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="escape_sequence">\u{48}</span><span class="escape_sequence">\x65</span><span class="escape_sequence">\x6C</span><span class="escape_sequence">\x6C</span><span class="escape_sequence">\x6F</span><span class="string_literal macro"> World"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
 
     <span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="string_literal">"</span><span class="escape_sequence">\x28</span><span class="escape_sequence">\x28</span><span class="escape_sequence">\x00</span><span class="escape_sequence">\x63</span><span class="escape_sequence">\n</span><span class="string_literal">"</span><span class="semicolon">;</span>
     <span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="string_literal">b"</span><span class="escape_sequence">\x28</span><span class="escape_sequence">\x28</span><span class="escape_sequence">\x00</span><span class="escape_sequence">\x63</span><span class="escape_sequence">\n</span><span class="string_literal">"</span><span class="semicolon">;</span>
+    <span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="string_literal">r"\\"</span><span class="semicolon">;</span>
 
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="escape_sequence">\x41</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> A <span class="operator">=</span> <span class="numeric_literal">92</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">ничоси</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> ничоси <span class="operator">=</span> <span class="numeric_literal">92</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="escape_sequence">\x41</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="none macro">A</span> <span class="operator macro">=</span> <span class="numeric_literal macro">92</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">ничоси</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="none macro">ничоси</span> <span class="operator macro">=</span> <span class="numeric_literal macro">92</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
 
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="variable">x</span><span class="format_specifier">?</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> "</span><span class="comma">,</span> <span class="unresolved_reference">thingy</span><span class="comma">,</span> <span class="unresolved_reference">n2</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro">panic</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="numeric_literal">0</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro">panic</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"more </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="numeric_literal">1</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro">assert</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="bool_literal">true</span><span class="comma">,</span> <span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="numeric_literal">1</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro">assert</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="bool_literal">true</span><span class="comma">,</span> <span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> asdasd"</span><span class="comma">,</span> <span class="numeric_literal">1</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro">toho</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">fmt"</span><span class="comma">,</span> <span class="numeric_literal">0</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro unsafe">asm</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"mov eax, </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro">format_args</span><span class="macro_bang">!</span><span class="parenthesis">(</span>concat<span class="punctuation">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="parenthesis">)</span><span class="comma">,</span> <span class="string_literal">"{}"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="variable">x</span><span class="format_specifier">?</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> "</span><span class="comma macro">,</span> <span class="unresolved_reference macro">thingy</span><span class="comma macro">,</span> <span class="unresolved_reference macro">n2</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro">panic</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro">panic</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"more </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro">assert</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="bool_literal macro">true</span><span class="comma macro">,</span> <span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro">assert</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="bool_literal macro">true</span><span class="comma macro">,</span> <span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> asdasd"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro">toho</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">fmt"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro unsafe">asm</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"mov eax, </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro">format_args</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="none macro">concat</span><span class="punctuation macro">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="comma macro">,</span> <span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
 <span class="brace">}</span></code></pre>
\ No newline at end of file
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
index 1992bdc6ae3..654d51b8a43 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
@@ -89,13 +89,13 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     <span class="keyword">let</span> <span class="variable declaration">x</span> <span class="operator">=</span> <span class="operator">&</span><span class="numeric_literal">5</span> <span class="keyword">as</span> <span class="keyword">*</span><span class="keyword">const</span> <span class="punctuation">_</span> <span class="keyword">as</span> <span class="keyword">*</span><span class="keyword">const</span> <span class="builtin_type">usize</span><span class="semicolon">;</span>
     <span class="keyword">let</span> <span class="variable declaration">u</span> <span class="operator">=</span> <span class="union">Union</span> <span class="brace">{</span> <span class="field">b</span><span class="colon">:</span> <span class="numeric_literal">0</span> <span class="brace">}</span><span class="semicolon">;</span>
 
-    <span class="macro">id</span><span class="macro_bang">!</span> <span class="brace">{</span>
-        <span class="keyword unsafe">unsafe</span> <span class="brace">{</span> <span class="macro unsafe">unsafe_deref</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">}</span>
-    <span class="brace">}</span><span class="semicolon">;</span>
+    <span class="macro">id</span><span class="macro_bang">!</span> <span class="brace macro">{</span>
+        <span class="keyword macro unsafe">unsafe</span> <span class="brace macro">{</span> <span class="macro macro unsafe">unsafe_deref</span><span class="macro_bang macro">!</span><span class="parenthesis macro">(</span><span class="parenthesis macro">)</span> <span class="brace macro">}</span>
+    <span class="brace macro">}</span><span class="semicolon">;</span>
 
     <span class="keyword unsafe">unsafe</span> <span class="brace">{</span>
-        <span class="macro unsafe">unsafe_deref</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-        <span class="macro unsafe">id</span><span class="macro_bang">!</span> <span class="brace">{</span> <span class="macro unsafe">unsafe_deref</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">}</span><span class="semicolon">;</span>
+        <span class="macro unsafe">unsafe_deref</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+        <span class="macro unsafe">id</span><span class="macro_bang">!</span> <span class="brace macro">{</span> <span class="macro macro unsafe">unsafe_deref</span><span class="macro_bang macro">!</span><span class="parenthesis macro">(</span><span class="parenthesis macro">)</span> <span class="brace macro">}</span><span class="semicolon">;</span>
 
         <span class="comment">// unsafe fn and method calls</span>
         <span class="function unsafe">unsafe_fn</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs
index ac9bd8e39dc..887d18b989a 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs
@@ -439,6 +439,16 @@ macro_rules! toho {
 }
 
 fn main() {
+    let a = '\n';
+    let a = '\t';
+    let a = '\e'; // invalid escape
+    let a = 'e';
+    let a = ' ';
+    let a = '\u{48}';
+    let a = '\u{4823}';
+    let a = '\x65';
+    let a = '\x00';
+
     println!("Hello {{Hello}}");
     // from https://doc.rust-lang.org/std/fmt/index.html
     println!("Hello");                 // => "Hello"
@@ -495,6 +505,7 @@ fn main() {
 
     let _ = "\x28\x28\x00\x63\n";
     let _ = b"\x28\x28\x00\x63\n";
+    let _ = r"\\";
 
     println!("{\x41}", A = 92);
     println!("{ничоси}", ничоси = 92);
@@ -1126,5 +1137,5 @@ fn benchmark_syntax_highlighting_parser() {
             .filter(|it| it.highlight.tag == HlTag::Symbol(SymbolKind::Function))
             .count()
     };
-    assert_eq!(hash, 1608);
+    assert_eq!(hash, 1169);
 }
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_tree.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_tree.rs
index bb6827e8a44..df197124265 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_tree.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_tree.rs
@@ -1,5 +1,7 @@
-use ide_db::base_db::{FileId, SourceDatabase};
-use ide_db::RootDatabase;
+use ide_db::{
+    base_db::{FileId, SourceDatabase},
+    RootDatabase,
+};
 use syntax::{
     AstNode, NodeOrToken, SourceFile, SyntaxKind::STRING, SyntaxToken, TextRange, TextSize,
 };
diff --git a/src/tools/rust-analyzer/crates/ide/src/view_crate_graph.rs b/src/tools/rust-analyzer/crates/ide/src/view_crate_graph.rs
index 17a1e385b77..8c84461f659 100644
--- a/src/tools/rust-analyzer/crates/ide/src/view_crate_graph.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/view_crate_graph.rs
@@ -1,11 +1,9 @@
-use std::sync::Arc;
-
 use dot::{Id, LabelText};
 use ide_db::{
     base_db::{CrateGraph, CrateId, Dependency, SourceDatabase, SourceDatabaseExt},
-    RootDatabase,
+    FxHashSet, RootDatabase,
 };
-use stdx::hash::NoHashHashSet;
+use triomphe::Arc;
 
 // Feature: View Crate Graph
 //
@@ -42,7 +40,7 @@ pub(crate) fn view_crate_graph(db: &RootDatabase, full: bool) -> Result<String,
 
 struct DotCrateGraph {
     graph: Arc<CrateGraph>,
-    crates_to_render: NoHashHashSet<CrateId>,
+    crates_to_render: FxHashSet<CrateId>,
 }
 
 type Edge<'a> = (CrateId, &'a Dependency);
@@ -80,7 +78,7 @@ impl<'a> dot::Labeller<'a, CrateId, Edge<'a>> for DotCrateGraph {
     }
 
     fn node_id(&'a self, n: &CrateId) -> Id<'a> {
-        Id::new(format!("_{}", n.0)).unwrap()
+        Id::new(format!("_{}", u32::from(n.into_raw()))).unwrap()
     }
 
     fn node_shape(&'a self, _node: &CrateId) -> Option<LabelText<'a>> {
diff --git a/src/tools/rust-analyzer/crates/ide/src/view_item_tree.rs b/src/tools/rust-analyzer/crates/ide/src/view_item_tree.rs
index 9c1f93356ee..e072df430fc 100644
--- a/src/tools/rust-analyzer/crates/ide/src/view_item_tree.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/view_item_tree.rs
@@ -12,5 +12,5 @@ use ide_db::RootDatabase;
 // | VS Code | **rust-analyzer: Debug ItemTree**
 // |===
 pub(crate) fn view_item_tree(db: &RootDatabase, file_id: FileId) -> String {
-    db.file_item_tree(file_id.into()).pretty_print()
+    db.file_item_tree(file_id.into()).pretty_print(db)
 }