diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index 307e22b0df1fd..b40066d05d355 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -4,3 +4,5 @@ a06baa56b95674fc626b3c3fd680d6a65357fe60 95e00bfed801e264e9c4ac817004153ca0f19eb6 # reformat with new rustfmt 971c549ca334b7b7406e61e958efcca9c4152822 +# refactor infcx building +283abbf0e7d20176f76006825b5c52e9a4234e4c diff --git a/.mailmap b/.mailmap index 564a68955d301..f887d29096e03 100644 --- a/.mailmap +++ b/.mailmap @@ -217,7 +217,7 @@ Hsiang-Cheng Yang Ian Jackson Ian Jackson Ian Jackson -Ibraheem Ahmed +Ibraheem Ahmed Ilyong Cho inquisitivecrystal <22333129+inquisitivecrystal@users.noreply.github.com> Irina Popa diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index 8ad40c0aa0a5d..7457369aa58cb 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -1,6 +1,4 @@ -use rustc_errors::{ - Applicability, Diagnostic, DiagnosticBuilder, EmissionGuarantee, ErrorGuaranteed, -}; +use rustc_errors::{Applicability, Diagnostic}; use rustc_hir as hir; use rustc_hir::intravisit::Visitor; use rustc_hir::Node; @@ -629,25 +627,20 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { self.buffer_error(err); } - fn suggest_map_index_mut_alternatives( - &self, - ty: Ty<'_>, - err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>, - span: Span, - ) { + fn suggest_map_index_mut_alternatives(&self, ty: Ty<'tcx>, err: &mut Diagnostic, span: Span) { let Some(adt) = ty.ty_adt_def() else { return }; let did = adt.did(); if self.infcx.tcx.is_diagnostic_item(sym::HashMap, did) || self.infcx.tcx.is_diagnostic_item(sym::BTreeMap, did) { - struct V<'a, 'b, 'tcx, G: EmissionGuarantee> { + struct V<'a, 'tcx> { assign_span: Span, - err: &'a mut DiagnosticBuilder<'b, G>, + err: &'a mut Diagnostic, ty: Ty<'tcx>, suggested: bool, } - impl<'a, 'b: 'a, 'hir, 'tcx, G: EmissionGuarantee> Visitor<'hir> for V<'a, 'b, 'tcx, G> { - fn visit_stmt(&mut self, stmt: &'hir hir::Stmt<'hir>) { + impl<'a, 'tcx> Visitor<'tcx> for V<'a, 'tcx> { + fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt<'tcx>) { hir::intravisit::walk_stmt(self, stmt); let expr = match stmt.kind { hir::StmtKind::Semi(expr) | hir::StmtKind::Expr(expr) => expr, @@ -705,7 +698,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { ), (rv.span.shrink_to_hi(), ")".to_string()), ], - ].into_iter(), + ], Applicability::MachineApplicable, ); self.suggested = true; diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 43101bbb9d31c..375e3ebd77df7 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -742,7 +742,7 @@ impl Diagnostic { &mut self, sp: Span, msg: impl Into, - suggestions: impl Iterator, + suggestions: impl IntoIterator, applicability: Applicability, ) -> &mut Self { self.span_suggestions_with_style( @@ -759,11 +759,11 @@ impl Diagnostic { &mut self, sp: Span, msg: impl Into, - suggestions: impl Iterator, + suggestions: impl IntoIterator, applicability: Applicability, style: SuggestionStyle, ) -> &mut Self { - let mut suggestions: Vec<_> = suggestions.collect(); + let mut suggestions: Vec<_> = suggestions.into_iter().collect(); suggestions.sort(); debug_assert!( @@ -790,10 +790,10 @@ impl Diagnostic { pub fn multipart_suggestions( &mut self, msg: impl Into, - suggestions: impl Iterator>, + suggestions: impl IntoIterator>, applicability: Applicability, ) -> &mut Self { - let suggestions: Vec<_> = suggestions.collect(); + let suggestions: Vec<_> = suggestions.into_iter().collect(); debug_assert!( !(suggestions .iter() diff --git a/compiler/rustc_errors/src/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs index 730061fca9936..18cbf963494fc 100644 --- a/compiler/rustc_errors/src/diagnostic_builder.rs +++ b/compiler/rustc_errors/src/diagnostic_builder.rs @@ -599,13 +599,13 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> { &mut self, sp: Span, msg: impl Into, - suggestions: impl Iterator, + suggestions: impl IntoIterator, applicability: Applicability, ) -> &mut Self); forward!(pub fn multipart_suggestions( &mut self, msg: impl Into, - suggestions: impl Iterator>, + suggestions: impl IntoIterator>, applicability: Applicability, ) -> &mut Self); forward!(pub fn span_suggestion_short( diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 799043842201a..43a5145b7e74d 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -1900,6 +1900,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { | ty::Str | ty::Projection(_) | ty::Param(_) => format!("{deref_ty}"), + // we need to test something like <&[_]>::len + // and Vec::function(); + // <&[_]>::len doesn't need an extra "<>" between + // but for Adt type like Vec::function() + // we would suggest <[_]>::function(); + _ if self.tcx.sess.source_map().span_wrapped_by_angle_bracket(ty.span) => format!("{deref_ty}"), _ => format!("<{deref_ty}>"), }; err.span_suggestion_verbose( diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index bda301c52e960..d657a2891171d 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -971,6 +971,23 @@ impl<'a> Parser<'a> { if self.eat(&token::ModSep) { self.parse_use_tree_glob_or_nested()? } else { + // Recover from using a colon as path separator. + while self.eat_noexpect(&token::Colon) { + self.struct_span_err(self.prev_token.span, "expected `::`, found `:`") + .span_suggestion_short( + self.prev_token.span, + "use double colon", + "::", + Applicability::MachineApplicable, + ) + .note_once("import paths are delimited using `::`") + .emit(); + + // We parse the rest of the path and append it to the original prefix. + self.parse_path_segments(&mut prefix.segments, PathStyle::Mod, None)?; + prefix.span = lo.to(self.prev_token.span); + } + UseTreeKind::Simple(self.parse_rename()?, DUMMY_NODE_ID, DUMMY_NODE_ID) } }; diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 2a8512acf8cfa..3a67c032b3bdd 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -401,7 +401,7 @@ impl<'a> Parser<'a> { .span_suggestions( span.shrink_to_hi(), "add `mut` or `const` here", - ["mut ".to_string(), "const ".to_string()].into_iter(), + ["mut ".to_string(), "const ".to_string()], Applicability::HasPlaceholders, ) .emit(); diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs index c6fe40f72fc63..c181de48a9ad8 100644 --- a/compiler/rustc_passes/src/liveness.rs +++ b/compiler/rustc_passes/src/liveness.rs @@ -87,6 +87,7 @@ use self::VarKind::*; use rustc_ast::InlineAsmOptions; use rustc_data_structures::fx::FxIndexMap; use rustc_errors::Applicability; +use rustc_errors::Diagnostic; use rustc_hir as hir; use rustc_hir::def::*; use rustc_hir::def_id::{DefId, LocalDefId}; @@ -1690,7 +1691,7 @@ impl<'tcx> Liveness<'_, 'tcx> { &self, name: &str, opt_body: Option<&hir::Body<'_>>, - err: &mut rustc_errors::DiagnosticBuilder<'_, ()>, + err: &mut Diagnostic, ) -> bool { let mut has_litstring = false; let Some(opt_body) = opt_body else {return false;}; diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index a1338dcd4771b..cdc28db13f3ac 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -437,7 +437,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { fn try_lookup_name_relaxed( &mut self, - err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>, + err: &mut Diagnostic, source: PathSource<'_>, path: &[Segment], span: Span, @@ -497,7 +497,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { .contains(span) { // Already reported this issue on the lhs of the type ascription. - err.delay_as_bug(); + err.downgrade_to_delayed_bug(); return (true, candidates); } } @@ -616,7 +616,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { fn suggest_trait_and_bounds( &mut self, - err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>, + err: &mut Diagnostic, source: PathSource<'_>, res: Option, span: Span, @@ -691,7 +691,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { fn suggest_typo( &mut self, - err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>, + err: &mut Diagnostic, source: PathSource<'_>, path: &[Segment], span: Span, @@ -750,7 +750,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { fn err_code_special_cases( &mut self, - err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>, + err: &mut Diagnostic, source: PathSource<'_>, path: &[Segment], span: Span, @@ -1941,7 +1941,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { err.span_suggestions( span, &msg, - suggestable_variants.into_iter(), + suggestable_variants, Applicability::MaybeIncorrect, ); } @@ -1995,7 +1995,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { err.span_suggestions( span, msg, - suggestable_variants.into_iter(), + suggestable_variants, Applicability::MaybeIncorrect, ); } @@ -2025,7 +2025,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { err.span_suggestions( span, msg, - suggestable_variants_with_placeholders.into_iter(), + suggestable_variants_with_placeholders, Applicability::HasPlaceholders, ); } diff --git a/compiler/rustc_span/src/source_map.rs b/compiler/rustc_span/src/source_map.rs index 3baa2e03cbad7..7ea028b12efc4 100644 --- a/compiler/rustc_span/src/source_map.rs +++ b/compiler/rustc_span/src/source_map.rs @@ -753,6 +753,50 @@ impl SourceMap { } } + /// Given a 'Span', tries to tell if the next character is '>' + /// and the previous charactoer is '<' after skipping white space + /// return true if wrapped by '<>' + pub fn span_wrapped_by_angle_bracket(&self, span: Span) -> bool { + self.span_to_source(span, |src, start_index, end_index| { + if src.get(start_index..end_index).is_none() { + return Ok(false); + } + // test the right side to match '>' after skipping white space + let end_src = &src[end_index..]; + let mut i = 0; + while let Some(cc) = end_src.chars().nth(i) { + if cc == ' ' { + i = i + 1; + } else if cc == '>' { + // found > in the right; + break; + } else { + // failed to find '>' return false immediately + return Ok(false); + } + } + // test the left side to match '<' after skipping white space + i = start_index; + let start_src = &src[0..start_index]; + while let Some(cc) = start_src.chars().nth(i) { + if cc == ' ' { + if i == 0 { + return Ok(false); + } + i = i - 1; + } else if cc == '<' { + // found < in the left + break; + } else { + // failed to find '<' return false immediately + return Ok(false); + } + } + return Ok(true); + }) + .map_or(false, |is_accessible| is_accessible) + } + /// Given a `Span`, tries to get a shorter span ending just after the first occurrence of `char` /// `c`. pub fn span_through_char(&self, sp: Span, c: char) -> Span { diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index aece4ab792285..540b1fa2b0fb7 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -1116,7 +1116,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { err.span_suggestions( span.shrink_to_lo(), "consider borrowing here", - ["&".to_string(), "&mut ".to_string()].into_iter(), + ["&".to_string(), "&mut ".to_string()], Applicability::MaybeIncorrect, ); } else { diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index 406bae02d84da..6fd363935079d 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -755,6 +755,7 @@ impl<'a> Builder<'a> { run::BuildManifest, run::BumpStage0, run::ReplaceVersionPlaceholder, + run::Miri, ), // These commands either don't use paths, or they're special-cased in Build::build() Kind::Clean | Kind::Format | Kind::Setup => vec![], @@ -818,7 +819,7 @@ impl<'a> Builder<'a> { Subcommand::Bench { ref paths, .. } => (Kind::Bench, &paths[..]), Subcommand::Dist { ref paths } => (Kind::Dist, &paths[..]), Subcommand::Install { ref paths } => (Kind::Install, &paths[..]), - Subcommand::Run { ref paths } => (Kind::Run, &paths[..]), + Subcommand::Run { ref paths, .. } => (Kind::Run, &paths[..]), Subcommand::Format { .. } => (Kind::Format, &[][..]), Subcommand::Clean { .. } | Subcommand::Setup { .. } => { panic!() diff --git a/src/bootstrap/flags.rs b/src/bootstrap/flags.rs index ee341a353ac47..2001e29bd2ead 100644 --- a/src/bootstrap/flags.rs +++ b/src/bootstrap/flags.rs @@ -140,6 +140,7 @@ pub enum Subcommand { }, Run { paths: Vec, + args: Vec, }, Setup { profile: Profile, @@ -342,6 +343,9 @@ To learn more about a subcommand, run `./x.py -h`", Kind::Format => { opts.optflag("", "check", "check formatting instead of applying."); } + Kind::Run => { + opts.optmulti("", "args", "arguments for the tool", "ARGS"); + } _ => {} }; @@ -613,7 +617,7 @@ Arguments: println!("\nrun requires at least a path!\n"); usage(1, &opts, verbose, &subcommand_help); } - Subcommand::Run { paths } + Subcommand::Run { paths, args: matches.opt_strs("args") } } Kind::Setup => { let profile = if paths.len() > 1 { @@ -721,16 +725,12 @@ impl Subcommand { } pub fn test_args(&self) -> Vec<&str> { - let mut args = vec![]; - match *self { Subcommand::Test { ref test_args, .. } | Subcommand::Bench { ref test_args, .. } => { - args.extend(test_args.iter().flat_map(|s| s.split_whitespace())) + test_args.iter().flat_map(|s| s.split_whitespace()).collect() } - _ => (), + _ => vec![], } - - args } pub fn rustc_args(&self) -> Vec<&str> { @@ -738,7 +738,16 @@ impl Subcommand { Subcommand::Test { ref rustc_args, .. } => { rustc_args.iter().flat_map(|s| s.split_whitespace()).collect() } - _ => Vec::new(), + _ => vec![], + } + } + + pub fn args(&self) -> Vec<&str> { + match *self { + Subcommand::Run { ref args, .. } => { + args.iter().flat_map(|s| s.split_whitespace()).collect() + } + _ => vec![], } } diff --git a/src/bootstrap/run.rs b/src/bootstrap/run.rs index 511872903d14e..d49b41c513279 100644 --- a/src/bootstrap/run.rs +++ b/src/bootstrap/run.rs @@ -1,8 +1,12 @@ +use std::process::Command; + use crate::builder::{Builder, RunConfig, ShouldRun, Step}; +use crate::config::TargetSelection; use crate::dist::distdir; -use crate::tool::Tool; +use crate::test; +use crate::tool::{self, SourceType, Tool}; use crate::util::output; -use std::process::Command; +use crate::Mode; #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct ExpandYamlAnchors; @@ -125,3 +129,63 @@ impl Step for ReplaceVersionPlaceholder { builder.run(&mut cmd); } } + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub struct Miri { + stage: u32, + host: TargetSelection, + target: TargetSelection, +} + +impl Step for Miri { + type Output = (); + const ONLY_HOSTS: bool = false; + + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.path("src/tools/miri") + } + + fn make_run(run: RunConfig<'_>) { + run.builder.ensure(Miri { + stage: run.builder.top_stage, + host: run.build_triple(), + target: run.target, + }); + } + + fn run(self, builder: &Builder<'_>) { + let stage = self.stage; + let host = self.host; + let target = self.target; + let compiler = builder.compiler(stage, host); + + let miri = builder + .ensure(tool::Miri { compiler, target: self.host, extra_features: Vec::new() }) + .expect("in-tree tool"); + let miri_sysroot = test::Miri::build_miri_sysroot(builder, compiler, &miri, target); + + // # Run miri. + // Running it via `cargo run` as that figures out the right dylib path. + // add_rustc_lib_path does not add the path that contains librustc_driver-<...>.so. + let mut miri = tool::prepare_tool_cargo( + builder, + compiler, + Mode::ToolRustc, + host, + "run", + "src/tools/miri", + SourceType::InTree, + &[], + ); + miri.add_rustc_lib_path(builder, compiler); + // Forward arguments. + miri.arg("--").arg("--target").arg(target.rustc_target_arg()); + miri.args(builder.config.cmd.args()); + + // miri tests need to know about the stage sysroot + miri.env("MIRI_SYSROOT", &miri_sysroot); + + let mut miri = Command::from(miri); + builder.run(&mut miri); + } +} diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs index 931d9a67944df..2b2257b72ae79 100644 --- a/src/bootstrap/test.rs +++ b/src/bootstrap/test.rs @@ -465,49 +465,20 @@ pub struct Miri { target: TargetSelection, } -impl Step for Miri { - type Output = (); - const ONLY_HOSTS: bool = false; - - fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - run.path("src/tools/miri") - } - - fn make_run(run: RunConfig<'_>) { - run.builder.ensure(Miri { - stage: run.builder.top_stage, - host: run.build_triple(), - target: run.target, - }); - } - - /// Runs `cargo test` for miri. - fn run(self, builder: &Builder<'_>) { - let stage = self.stage; - let host = self.host; - let target = self.target; - let compiler = builder.compiler(stage, host); - // We need the stdlib for the *next* stage, as it was built with this compiler that also built Miri. - // Except if we are at stage 2, the bootstrap loop is complete and we can stick with our current stage. - let compiler_std = builder.compiler(if stage < 2 { stage + 1 } else { stage }, host); - - let miri = builder - .ensure(tool::Miri { compiler, target: self.host, extra_features: Vec::new() }) - .expect("in-tree tool"); - let _cargo_miri = builder - .ensure(tool::CargoMiri { compiler, target: self.host, extra_features: Vec::new() }) - .expect("in-tree tool"); - // The stdlib we need might be at a different stage. And just asking for the - // sysroot does not seem to populate it, so we do that first. - builder.ensure(compile::Std::new(compiler_std, host)); - let sysroot = builder.sysroot(compiler_std); - - // # Run `cargo miri setup` for the given target. +impl Miri { + /// Run `cargo miri setup` for the given target, return where the Miri sysroot was put. + pub fn build_miri_sysroot( + builder: &Builder<'_>, + compiler: Compiler, + miri: &Path, + target: TargetSelection, + ) -> String { + let miri_sysroot = builder.out.join(compiler.host.triple).join("miri-sysrot"); let mut cargo = tool::prepare_tool_cargo( builder, compiler, Mode::ToolRustc, - host, + compiler.host, "run", "src/tools/miri/cargo-miri", SourceType::InTree, @@ -521,6 +492,8 @@ impl Step for Miri { cargo.env("MIRI_LIB_SRC", builder.src.join("library")); // Tell it where to find Miri. cargo.env("MIRI", &miri); + // Tell it where to put the sysroot. + cargo.env("MIRI_SYSROOT", &miri_sysroot); // Debug things. cargo.env("RUST_BACKTRACE", "1"); @@ -535,7 +508,7 @@ impl Step for Miri { cargo.arg("--print-sysroot"); // FIXME: Is there a way in which we can re-use the usual `run` helpers? - let miri_sysroot = if builder.config.dry_run { + if builder.config.dry_run { String::new() } else { builder.verbose(&format!("running: {:?}", cargo)); @@ -548,7 +521,48 @@ impl Step for Miri { let sysroot = stdout.trim_end(); builder.verbose(&format!("`cargo miri setup --print-sysroot` said: {:?}", sysroot)); sysroot.to_owned() - }; + } + } +} + +impl Step for Miri { + type Output = (); + const ONLY_HOSTS: bool = false; + + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.path("src/tools/miri") + } + + fn make_run(run: RunConfig<'_>) { + run.builder.ensure(Miri { + stage: run.builder.top_stage, + host: run.build_triple(), + target: run.target, + }); + } + + /// Runs `cargo test` for miri. + fn run(self, builder: &Builder<'_>) { + let stage = self.stage; + let host = self.host; + let target = self.target; + let compiler = builder.compiler(stage, host); + // We need the stdlib for the *next* stage, as it was built with this compiler that also built Miri. + // Except if we are at stage 2, the bootstrap loop is complete and we can stick with our current stage. + let compiler_std = builder.compiler(if stage < 2 { stage + 1 } else { stage }, host); + + let miri = builder + .ensure(tool::Miri { compiler, target: self.host, extra_features: Vec::new() }) + .expect("in-tree tool"); + let _cargo_miri = builder + .ensure(tool::CargoMiri { compiler, target: self.host, extra_features: Vec::new() }) + .expect("in-tree tool"); + // The stdlib we need might be at a different stage. And just asking for the + // sysroot does not seem to populate it, so we do that first. + builder.ensure(compile::Std::new(compiler_std, host)); + let sysroot = builder.sysroot(compiler_std); + // We also need a Miri sysroot. + let miri_sysroot = Miri::build_miri_sysroot(builder, compiler, &miri, target); // # Run `cargo test`. let mut cargo = tool::prepare_tool_cargo( @@ -566,7 +580,6 @@ impl Step for Miri { // miri tests need to know about the stage sysroot cargo.env("MIRI_SYSROOT", &miri_sysroot); cargo.env("MIRI_HOST_SYSROOT", sysroot); - cargo.env("RUSTC_LIB_PATH", builder.rustc_libdir(compiler)); cargo.env("MIRI", &miri); // propagate --bless if builder.config.cmd.bless() { @@ -607,7 +620,6 @@ impl Step for Miri { // Tell `cargo miri` where to find things. cargo.env("MIRI_SYSROOT", &miri_sysroot); cargo.env("MIRI_HOST_SYSROOT", sysroot); - cargo.env("RUSTC_LIB_PATH", builder.rustc_libdir(compiler)); cargo.env("MIRI", &miri); // Debug things. cargo.env("RUST_BACKTRACE", "1"); diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version b/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version index cc96715b28572..ed0d9e9902b99 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version +++ b/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version @@ -1 +1 @@ -0.12.7 \ No newline at end of file +0.13.1 \ No newline at end of file diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index f58d2c609426d..86a5b0b303f39 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -830,6 +830,9 @@ table, line-height: 1.5; font-weight: 500; } +#crate-search:hover, #crate-search:focus { + border-color: var(--crate-search-hover-border); +} /* cancel stylistic differences in padding in firefox for "appearance: none"-style (or equivalent) ` isn't bigger than its container (".search-results-title"). assert-css: ("#search", {"width": "640px"}) + +// Now checking that the crate filter is working as expected too. +show-text: true +define-function: ( + "check-filter", + (theme, border, filter, hover_border, hover_filter), + [ + ("local-storage", {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"}), + ("reload"), + ("wait-for", "#crate-search"), + ("assert-css", ("#crate-search", {"border": "1px solid " + |border|})), + ("assert-css", ("#crate-search-div::after", {"filter": |filter|})), + ("move-cursor-to", "#crate-search"), + ("assert-css", ("#crate-search", {"border": "1px solid " + |hover_border|})), + ("assert-css", ("#crate-search-div::after", {"filter": |hover_filter|})), + ("move-cursor-to", ".search-input"), + ], +) + +call-function: ("check-filter", { + "theme": "ayu", + "border": "rgb(92, 103, 115)", + "filter": "invert(0.41) sepia(0.12) saturate(4.87) hue-rotate(171deg) brightness(0.94) contrast(0.94)", + "hover_border": "rgb(224, 224, 224)", + "hover_filter": "invert(0.98) sepia(0.12) saturate(0.81) hue-rotate(343deg) brightness(1.13) contrast(0.76)", +}) +call-function: ("check-filter", { + "theme": "dark", + "border": "rgb(224, 224, 224)", + "filter": "invert(0.94) sepia(0) saturate(7.21) hue-rotate(255deg) brightness(0.9) contrast(0.9)", + "hover_border": "rgb(33, 150, 243)", + "hover_filter": "invert(0.69) sepia(0.6) saturate(66.13) hue-rotate(184deg) brightness(1) contrast(0.91)", +}) +call-function: ("check-filter", { + "theme": "light", + "border": "rgb(224, 224, 224)", + "filter": "invert(1) sepia(0) saturate(42.23) hue-rotate(289deg) brightness(1.14) contrast(0.76)", + "hover_border": "rgb(113, 113, 113)", + "hover_filter": "invert(0.44) sepia(0.18) saturate(0.23) hue-rotate(317deg) brightness(0.96) contrast(0.93)", +}) diff --git a/src/test/rustdoc-gui/trait-sidebar-item-order.goml b/src/test/rustdoc-gui/trait-sidebar-item-order.goml index a799444a1087e..e5d023544d682 100644 --- a/src/test/rustdoc-gui/trait-sidebar-item-order.goml +++ b/src/test/rustdoc-gui/trait-sidebar-item-order.goml @@ -1,4 +1,9 @@ // Checks that the elements in the sidebar are alphabetically sorted. + +// We need to disable this check because `implementors/test_docs/trait.AnotherOne.js` +// doesn't exist. +fail-on-request-error: false + goto: "file://" + |DOC_PATH| + "/test_docs/trait.AnotherOne.html" assert-text: (".sidebar-elems section .block li:nth-of-type(1) > a", "another") assert-text: (".sidebar-elems section .block li:nth-of-type(2) > a", "func1") diff --git a/src/test/rustdoc-gui/type-declation-overflow.goml b/src/test/rustdoc-gui/type-declation-overflow.goml index fce3002e7508f..dcffe956c2197 100644 --- a/src/test/rustdoc-gui/type-declation-overflow.goml +++ b/src/test/rustdoc-gui/type-declation-overflow.goml @@ -1,4 +1,10 @@ // This test ensures that the items declaration content overflow is handled inside the
 directly.
+
+// We need to disable this check because
+// `implementors/test_docs/trait.ALongNameBecauseItHelpsTestingTheCurrentProblem.js`
+// doesn't exist.
+fail-on-request-error: false
+
 goto: "file://" + |DOC_PATH| + "/lib2/long_trait/trait.ALongNameBecauseItHelpsTestingTheCurrentProblem.html"
 // We set a fixed size so there is no chance of "random" resize.
 size: (1100, 800)
diff --git a/src/test/ui/parser/use-colon-as-mod-sep.rs b/src/test/ui/parser/use-colon-as-mod-sep.rs
new file mode 100644
index 0000000000000..e1e8756b03c26
--- /dev/null
+++ b/src/test/ui/parser/use-colon-as-mod-sep.rs
@@ -0,0 +1,11 @@
+// Recover from using a colon as a path separator.
+
+use std::process:Command;
+//~^ ERROR expected `::`, found `:`
+use std:fs::File;
+//~^ ERROR expected `::`, found `:`
+use std:collections:HashMap;
+//~^ ERROR expected `::`, found `:`
+//~| ERROR expected `::`, found `:`
+
+fn main() { }
diff --git a/src/test/ui/parser/use-colon-as-mod-sep.stderr b/src/test/ui/parser/use-colon-as-mod-sep.stderr
new file mode 100644
index 0000000000000..e825dfed11192
--- /dev/null
+++ b/src/test/ui/parser/use-colon-as-mod-sep.stderr
@@ -0,0 +1,28 @@
+error: expected `::`, found `:`
+  --> $DIR/use-colon-as-mod-sep.rs:3:17
+   |
+LL | use std::process:Command;
+   |                 ^ help: use double colon
+   |
+   = note: import paths are delimited using `::`
+
+error: expected `::`, found `:`
+  --> $DIR/use-colon-as-mod-sep.rs:5:8
+   |
+LL | use std:fs::File;
+   |        ^ help: use double colon
+
+error: expected `::`, found `:`
+  --> $DIR/use-colon-as-mod-sep.rs:7:8
+   |
+LL | use std:collections:HashMap;
+   |        ^ help: use double colon
+
+error: expected `::`, found `:`
+  --> $DIR/use-colon-as-mod-sep.rs:7:20
+   |
+LL | use std:collections:HashMap;
+   |                    ^ help: use double colon
+
+error: aborting due to 4 previous errors
+
diff --git a/src/test/ui/type/issue-103271.rs b/src/test/ui/type/issue-103271.rs
new file mode 100644
index 0000000000000..bd3254af3df71
--- /dev/null
+++ b/src/test/ui/type/issue-103271.rs
@@ -0,0 +1,10 @@
+fn main() {
+    let iter_fun = <&[u32]>::iter;
+    //~^ ERROR no function or associated item named `iter` found for reference `&[u32]` in the current scope [E0599]
+    //~| function or associated item not found in `&[u32]`
+    //~| HELP the function `iter` is implemented on `[u32]`
+    for item in iter_fun(&[1,1]) {
+        let x: &u32 = item;
+        assert_eq!(x, &1);
+    }
+}
diff --git a/src/test/ui/type/issue-103271.stderr b/src/test/ui/type/issue-103271.stderr
new file mode 100644
index 0000000000000..02a59d4b99c4d
--- /dev/null
+++ b/src/test/ui/type/issue-103271.stderr
@@ -0,0 +1,14 @@
+error[E0599]: no function or associated item named `iter` found for reference `&[u32]` in the current scope
+  --> $DIR/issue-103271.rs:2:30
+   |
+LL |     let iter_fun = <&[u32]>::iter;
+   |                              ^^^^ function or associated item not found in `&[u32]`
+   |
+help: the function `iter` is implemented on `[u32]`
+   |
+LL |     let iter_fun = <[u32]>::iter;
+   |                     ~~~~~
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0599`.
diff --git a/src/tools/miri/README.md b/src/tools/miri/README.md
index 5803a88c0e757..f5a20d592d06d 100644
--- a/src/tools/miri/README.md
+++ b/src/tools/miri/README.md
@@ -433,8 +433,10 @@ Moreover, Miri recognizes some environment variables:
   trigger a re-build of the standard library; you have to clear the Miri build
   cache manually (on Linux, `rm -rf ~/.cache/miri`).
 * `MIRI_SYSROOT` (recognized by `cargo miri` and the Miri driver) indicates the sysroot to use. When
-  using `cargo miri`, only set this if you do not want to use the automatically created sysroot. For
-  directly invoking the Miri driver, this variable (or a `--sysroot` flag) is mandatory.
+  using `cargo miri`, this skips the automatic setup -- only set this if you do not want to use the
+  automatically created sysroot. For directly invoking the Miri driver, this variable (or a
+  `--sysroot` flag) is mandatory. When invoking `cargo miri setup`, this indicates where the sysroot
+  will be put.
 * `MIRI_TEST_TARGET` (recognized by the test suite and the `./miri` script) indicates which target
   architecture to test against.  `miri` and `cargo miri` accept the `--target` flag for the same
   purpose.
diff --git a/src/tools/miri/cargo-miri/src/setup.rs b/src/tools/miri/cargo-miri/src/setup.rs
index 72d8ef2f75224..f3841a6140839 100644
--- a/src/tools/miri/cargo-miri/src/setup.rs
+++ b/src/tools/miri/cargo-miri/src/setup.rs
@@ -17,10 +17,8 @@ pub fn setup(subcommand: &MiriCommand, target: &str, rustc_version: &VersionMeta
     let only_setup = matches!(subcommand, MiriCommand::Setup);
     let ask_user = !only_setup;
     let print_sysroot = only_setup && has_arg_flag("--print-sysroot"); // whether we just print the sysroot path
-    if std::env::var_os("MIRI_SYSROOT").is_some() {
-        if only_setup {
-            println!("WARNING: MIRI_SYSROOT already set, not doing anything.")
-        }
+    if !only_setup && std::env::var_os("MIRI_SYSROOT").is_some() {
+        // Skip setup step if MIRI_SYSROOT is explicitly set, *unless* we are `cargo miri setup`.
         return;
     }
 
@@ -61,8 +59,13 @@ pub fn setup(subcommand: &MiriCommand, target: &str, rustc_version: &VersionMeta
     }
 
     // Determine where to put the sysroot.
-    let user_dirs = directories::ProjectDirs::from("org", "rust-lang", "miri").unwrap();
-    let sysroot_dir = user_dirs.cache_dir();
+    let sysroot_dir = match std::env::var_os("MIRI_SYSROOT") {
+        Some(dir) => PathBuf::from(dir),
+        None => {
+            let user_dirs = directories::ProjectDirs::from("org", "rust-lang", "miri").unwrap();
+            user_dirs.cache_dir().to_owned()
+        }
+    };
     // Sysroot configuration and build details.
     let sysroot_config = if std::env::var_os("MIRI_NO_STD").is_some() {
         SysrootConfig::NoStd
@@ -111,7 +114,7 @@ pub fn setup(subcommand: &MiriCommand, target: &str, rustc_version: &VersionMeta
         (command, rustflags)
     };
     // Make sure all target-level Miri invocations know their sysroot.
-    std::env::set_var("MIRI_SYSROOT", sysroot_dir);
+    std::env::set_var("MIRI_SYSROOT", &sysroot_dir);
 
     // Do the build.
     if only_setup {
@@ -121,7 +124,7 @@ pub fn setup(subcommand: &MiriCommand, target: &str, rustc_version: &VersionMeta
         // We want to be quiet, but still let the user know that something is happening.
         eprint!("Preparing a sysroot for Miri (target: {target})... ");
     }
-    Sysroot::new(sysroot_dir, target)
+    Sysroot::new(&sysroot_dir, target)
         .build_from_source(&rust_src, BuildMode::Check, sysroot_config, rustc_version, cargo_cmd)
         .unwrap_or_else(|_| {
             if only_setup {
diff --git a/src/tools/rust-analyzer/.github/ISSUE_TEMPLATE/bug_report.md b/src/tools/rust-analyzer/.github/ISSUE_TEMPLATE/bug_report.md
index a038dce3248aa..c2e21933c9a68 100644
--- a/src/tools/rust-analyzer/.github/ISSUE_TEMPLATE/bug_report.md
+++ b/src/tools/rust-analyzer/.github/ISSUE_TEMPLATE/bug_report.md
@@ -13,8 +13,7 @@ Forum for questions: https://users.rust-lang.org/c/ide/14
 
 Before submitting, please make sure that you're not running into one of these known issues:
 
- 1. extension doesn't load in VSCodium: #11080
- 2. on-the-fly diagnostics are mostly unimplemented (`cargo check` diagnostics will be shown when saving a file): #3107
+ 1. on-the-fly diagnostics are mostly unimplemented (`cargo check` diagnostics will be shown when saving a file): #3107
 
 Otherwise please try to provide information which will help us to fix the issue faster. Minimal reproducible examples with few dependencies are especially lovely <3.
 -->
diff --git a/src/tools/rust-analyzer/.github/ISSUE_TEMPLATE/critical_nightly_regression.md b/src/tools/rust-analyzer/.github/ISSUE_TEMPLATE/critical_nightly_regression.md
index a0b1627d7e2ef..ad220ff65ca14 100644
--- a/src/tools/rust-analyzer/.github/ISSUE_TEMPLATE/critical_nightly_regression.md
+++ b/src/tools/rust-analyzer/.github/ISSUE_TEMPLATE/critical_nightly_regression.md
@@ -2,8 +2,8 @@
 name: Critical Nightly Regression
 about: You are using nightly rust-analyzer and the latest version is unusable.
 title: ''
-labels: ''
-assignees: 'matklad'
+labels: 'Broken Window'
+assignees: ''
 
 ---
 
@@ -14,4 +14,3 @@ Please try to provide information which will help us to fix the issue faster. Mi
 -->
 
 This is a serious regression in nightly and it's important to fix it before the next release.
-@matklad, please take a look.
diff --git a/src/tools/rust-analyzer/.github/workflows/release.yaml b/src/tools/rust-analyzer/.github/workflows/release.yaml
index 422fe29f9d5c3..b070dd3406f20 100644
--- a/src/tools/rust-analyzer/.github/workflows/release.yaml
+++ b/src/tools/rust-analyzer/.github/workflows/release.yaml
@@ -257,8 +257,7 @@ jobs:
       - name: Publish Extension (OpenVSX, release)
         if: github.ref == 'refs/heads/release' && (github.repository == 'rust-analyzer/rust-analyzer' || github.repository == 'rust-lang/rust-analyzer')
         working-directory: ./editors/code
-        # token from https://dev.azure.com/rust-analyzer/
-        run: npx ovsx publish --pat ${{ secrets.OPENVSX_TOKEN }} --packagePath ../../dist/rust-analyzer-*.vsix || true
+        run: npx ovsx publish --pat ${{ secrets.OPENVSX_TOKEN }} --packagePath ../../dist/rust-analyzer-*.vsix
         timeout-minutes: 2
 
       - name: Publish Extension (Code Marketplace, nightly)
@@ -269,5 +268,5 @@ jobs:
       - name: Publish Extension (OpenVSX, nightly)
         if: github.ref != 'refs/heads/release' && (github.repository == 'rust-analyzer/rust-analyzer' || github.repository == 'rust-lang/rust-analyzer')
         working-directory: ./editors/code
-        run: npx ovsx publish --pat ${{ secrets.OPENVSX_TOKEN }} --packagePath ../../dist/rust-analyzer-*.vsix || true
+        run: npx ovsx publish --pat ${{ secrets.OPENVSX_TOKEN }} --packagePath ../../dist/rust-analyzer-*.vsix
         timeout-minutes: 2
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/pretty.rs
index 6636c8a23ca5f..933970d10e472 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/pretty.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/pretty.rs
@@ -143,9 +143,12 @@ pub(crate) fn print_type_ref(type_ref: &TypeRef, buf: &mut dyn Write) -> fmt::Re
             print_type_ref(elem, buf)?;
             write!(buf, "]")?;
         }
-        TypeRef::Fn(args_and_ret, varargs) => {
+        TypeRef::Fn(args_and_ret, varargs, is_unsafe) => {
             let ((_, return_type), args) =
                 args_and_ret.split_last().expect("TypeRef::Fn is missing return type");
+            if *is_unsafe {
+                write!(buf, "unsafe ")?;
+            }
             write!(buf, "fn(")?;
             for (i, (_, typeref)) in args.iter().enumerate() {
                 if i != 0 {
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/type_ref.rs b/src/tools/rust-analyzer/crates/hir-def/src/type_ref.rs
index 5b4c71be7fb83..f8bb78ddcfe02 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/type_ref.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/type_ref.rs
@@ -119,7 +119,7 @@ pub enum TypeRef {
     Array(Box, ConstScalarOrPath),
     Slice(Box),
     /// A fn pointer. Last element of the vector is the return type.
-    Fn(Vec<(Option, TypeRef)>, bool /*varargs*/),
+    Fn(Vec<(Option, TypeRef)>, bool /*varargs*/, bool /*is_unsafe*/),
     ImplTrait(Vec>),
     DynTrait(Vec>),
     Macro(AstId),
@@ -229,7 +229,7 @@ impl TypeRef {
                     Vec::new()
                 };
                 params.push((None, ret_ty));
-                TypeRef::Fn(params, is_varargs)
+                TypeRef::Fn(params, is_varargs, inner.unsafe_token().is_some())
             }
             // for types are close enough for our purposes to the inner type for now...
             ast::Type::ForType(inner) => TypeRef::from_ast_opt(ctx, inner.ty()),
@@ -263,7 +263,7 @@ impl TypeRef {
         fn go(type_ref: &TypeRef, f: &mut impl FnMut(&TypeRef)) {
             f(type_ref);
             match type_ref {
-                TypeRef::Fn(params, _) => {
+                TypeRef::Fn(params, _, _) => {
                     params.iter().for_each(|(_, param_type)| go(param_type, f))
                 }
                 TypeRef::Tuple(types) => types.iter().for_each(|t| go(t, f)),
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs
index 5ad6613263534..a22a4b170f61c 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs
@@ -1187,8 +1187,11 @@ impl HirDisplay for TypeRef {
                 inner.hir_fmt(f)?;
                 write!(f, "]")?;
             }
-            TypeRef::Fn(parameters, is_varargs) => {
+            &TypeRef::Fn(ref parameters, is_varargs, is_unsafe) => {
                 // FIXME: Function pointer qualifiers.
+                if is_unsafe {
+                    write!(f, "unsafe ")?;
+                }
                 write!(f, "fn(")?;
                 if let Some(((_, return_type), function_parameters)) = parameters.split_last() {
                     for index in 0..function_parameters.len() {
@@ -1203,7 +1206,7 @@ impl HirDisplay for TypeRef {
                             write!(f, ", ")?;
                         }
                     }
-                    if *is_varargs {
+                    if is_varargs {
                         write!(f, "{}...", if parameters.len() == 1 { "" } else { ", " })?;
                     }
                     write!(f, ")")?;
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
index 0efff651cc174..0b3c23f5747ad 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
@@ -1020,7 +1020,7 @@ impl Expectation {
     /// The primary use case is where the expected type is a fat pointer,
     /// like `&[isize]`. For example, consider the following statement:
     ///
-    ///    let x: &[isize] = &[1, 2, 3];
+    ///     let x: &[isize] = &[1, 2, 3];
     ///
     /// In this case, the expected type for the `&[1, 2, 3]` expression is
     /// `&[isize]`. If however we were to say that `[1, 2, 3]` has the
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs
index f56108b26c45b..b1f4de8260775 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs
@@ -85,6 +85,7 @@ impl<'a> InferenceContext<'a> {
         let ty = match &self.body[tgt_expr] {
             Expr::Missing => self.err_ty(),
             &Expr::If { condition, then_branch, else_branch } => {
+                let expected = &expected.adjust_for_branches(&mut self.table);
                 self.infer_expr(
                     condition,
                     &Expectation::has_type(TyKind::Scalar(Scalar::Bool).intern(Interner)),
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs
index 42c3b58d5ada5..b68c764bdca09 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs
@@ -38,10 +38,12 @@ use std::sync::Arc;
 use chalk_ir::{
     fold::{Shift, TypeFoldable},
     interner::HasInterner,
-    NoSolution,
+    NoSolution, UniverseIndex,
 };
 use hir_def::{expr::ExprId, type_ref::Rawness, TypeOrConstParamId};
+use hir_expand::name;
 use itertools::Either;
+use traits::FnTrait;
 use utils::Generics;
 
 use crate::{consteval::unknown_const, db::HirDatabase, utils::generics};
@@ -208,6 +210,7 @@ pub(crate) fn make_binders>(
 pub struct CallableSig {
     params_and_return: Arc<[Ty]>,
     is_varargs: bool,
+    safety: Safety,
 }
 
 has_interner!(CallableSig);
@@ -216,9 +219,14 @@ has_interner!(CallableSig);
 pub type PolyFnSig = Binders;
 
 impl CallableSig {
-    pub fn from_params_and_return(mut params: Vec, ret: Ty, is_varargs: bool) -> CallableSig {
+    pub fn from_params_and_return(
+        mut params: Vec,
+        ret: Ty,
+        is_varargs: bool,
+        safety: Safety,
+    ) -> CallableSig {
         params.push(ret);
-        CallableSig { params_and_return: params.into(), is_varargs }
+        CallableSig { params_and_return: params.into(), is_varargs, safety }
     }
 
     pub fn from_fn_ptr(fn_ptr: &FnPointer) -> CallableSig {
@@ -235,13 +243,14 @@ impl CallableSig {
                 .map(|arg| arg.assert_ty_ref(Interner).clone())
                 .collect(),
             is_varargs: fn_ptr.sig.variadic,
+            safety: fn_ptr.sig.safety,
         }
     }
 
     pub fn to_fn_ptr(&self) -> FnPointer {
         FnPointer {
             num_binders: 0,
-            sig: FnSig { abi: (), safety: Safety::Safe, variadic: self.is_varargs },
+            sig: FnSig { abi: (), safety: self.safety, variadic: self.is_varargs },
             substitution: FnSubst(Substitution::from_iter(
                 Interner,
                 self.params_and_return.iter().cloned(),
@@ -266,7 +275,11 @@ impl TypeFoldable for CallableSig {
     ) -> Result {
         let vec = self.params_and_return.to_vec();
         let folded = vec.try_fold_with(folder, outer_binder)?;
-        Ok(CallableSig { params_and_return: folded.into(), is_varargs: self.is_varargs })
+        Ok(CallableSig {
+            params_and_return: folded.into(),
+            is_varargs: self.is_varargs,
+            safety: self.safety,
+        })
     }
 }
 
@@ -508,3 +521,68 @@ where
     });
     Canonical { value, binders: chalk_ir::CanonicalVarKinds::from_iter(Interner, kinds) }
 }
+
+pub fn callable_sig_from_fnonce(
+    self_ty: &Canonical,
+    env: Arc,
+    db: &dyn HirDatabase,
+) -> Option {
+    let krate = env.krate;
+    let fn_once_trait = FnTrait::FnOnce.get_id(db, krate)?;
+    let output_assoc_type = db.trait_data(fn_once_trait).associated_type_by_name(&name![Output])?;
+
+    let mut kinds = self_ty.binders.interned().to_vec();
+    let b = TyBuilder::trait_ref(db, fn_once_trait);
+    if b.remaining() != 2 {
+        return None;
+    }
+    let fn_once = b
+        .push(self_ty.value.clone())
+        .fill_with_bound_vars(DebruijnIndex::INNERMOST, kinds.len())
+        .build();
+    kinds.extend(fn_once.substitution.iter(Interner).skip(1).map(|x| {
+        let vk = match x.data(Interner) {
+            chalk_ir::GenericArgData::Ty(_) => {
+                chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General)
+            }
+            chalk_ir::GenericArgData::Lifetime(_) => chalk_ir::VariableKind::Lifetime,
+            chalk_ir::GenericArgData::Const(c) => {
+                chalk_ir::VariableKind::Const(c.data(Interner).ty.clone())
+            }
+        };
+        chalk_ir::WithKind::new(vk, UniverseIndex::ROOT)
+    }));
+
+    // FIXME: chalk refuses to solve `>::Output == ^0.1`, so we first solve
+    // `>` and then replace `^0.0` with the concrete argument tuple.
+    let trait_env = env.env.clone();
+    let obligation = InEnvironment { goal: fn_once.cast(Interner), environment: trait_env };
+    let canonical =
+        Canonical { binders: CanonicalVarKinds::from_iter(Interner, kinds), value: obligation };
+    let subst = match db.trait_solve(krate, canonical) {
+        Some(Solution::Unique(vars)) => vars.value.subst,
+        _ => return None,
+    };
+    let args = subst.at(Interner, self_ty.binders.interned().len()).ty(Interner)?;
+    let params = match args.kind(Interner) {
+        chalk_ir::TyKind::Tuple(_, subst) => {
+            subst.iter(Interner).filter_map(|arg| arg.ty(Interner).cloned()).collect::>()
+        }
+        _ => return None,
+    };
+    if params.iter().any(|ty| ty.is_unknown()) {
+        return None;
+    }
+
+    let fn_once = TyBuilder::trait_ref(db, fn_once_trait)
+        .push(self_ty.value.clone())
+        .push(args.clone())
+        .build();
+    let projection =
+        TyBuilder::assoc_type_projection(db, output_assoc_type, Some(fn_once.substitution.clone()))
+            .build();
+
+    let ret_ty = db.normalize_projection(projection, env);
+
+    Some(CallableSig::from_params_and_return(params, ret_ty.clone(), false, Safety::Safe))
+}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs
index 22a85cf154587..baf9842d5fbf2 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs
@@ -227,13 +227,17 @@ impl<'a> TyLoweringContext<'a> {
                     .intern(Interner)
             }
             TypeRef::Placeholder => TyKind::Error.intern(Interner),
-            TypeRef::Fn(params, is_varargs) => {
+            &TypeRef::Fn(ref params, variadic, is_unsafe) => {
                 let substs = self.with_shifted_in(DebruijnIndex::ONE, |ctx| {
                     Substitution::from_iter(Interner, params.iter().map(|(_, tr)| ctx.lower_ty(tr)))
                 });
                 TyKind::Function(FnPointer {
                     num_binders: 0, // FIXME lower `for<'a> fn()` correctly
-                    sig: FnSig { abi: (), safety: Safety::Safe, variadic: *is_varargs },
+                    sig: FnSig {
+                        abi: (),
+                        safety: if is_unsafe { Safety::Unsafe } else { Safety::Safe },
+                        variadic,
+                    },
                     substitution: FnSubst(substs),
                 })
                 .intern(Interner)
@@ -1573,7 +1577,12 @@ fn fn_sig_for_fn(db: &dyn HirDatabase, def: FunctionId) -> PolyFnSig {
         .with_type_param_mode(ParamLoweringMode::Variable);
     let ret = ctx_ret.lower_ty(&data.ret_type);
     let generics = generics(db.upcast(), def.into());
-    let sig = CallableSig::from_params_and_return(params, ret, data.is_varargs());
+    let sig = CallableSig::from_params_and_return(
+        params,
+        ret,
+        data.is_varargs(),
+        if data.has_unsafe_kw() { Safety::Unsafe } else { Safety::Safe },
+    );
     make_binders(db, &generics, sig)
 }
 
@@ -1617,7 +1626,7 @@ fn fn_sig_for_struct_constructor(db: &dyn HirDatabase, def: StructId) -> PolyFnS
         TyLoweringContext::new(db, &resolver).with_type_param_mode(ParamLoweringMode::Variable);
     let params = fields.iter().map(|(_, field)| ctx.lower_ty(&field.type_ref)).collect::>();
     let (ret, binders) = type_for_adt(db, def.into()).into_value_and_skipped_binders();
-    Binders::new(binders, CallableSig::from_params_and_return(params, ret, false))
+    Binders::new(binders, CallableSig::from_params_and_return(params, ret, false, Safety::Safe))
 }
 
 /// Build the type of a tuple struct constructor.
@@ -1644,7 +1653,7 @@ fn fn_sig_for_enum_variant_constructor(db: &dyn HirDatabase, def: EnumVariantId)
         TyLoweringContext::new(db, &resolver).with_type_param_mode(ParamLoweringMode::Variable);
     let params = fields.iter().map(|(_, field)| ctx.lower_ty(&field.type_ref)).collect::>();
     let (ret, binders) = type_for_adt(db, def.parent.into()).into_value_and_skipped_binders();
-    Binders::new(binders, CallableSig::from_params_and_return(params, ret, false))
+    Binders::new(binders, CallableSig::from_params_and_return(params, ret, false, Safety::Safe))
 }
 
 /// Build the type of a tuple enum variant constructor.
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs
index d301595bcd98a..7e3aecc2ae0ae 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs
@@ -122,6 +122,23 @@ fn test() {
     )
 }
 
+#[test]
+fn if_else_adjust_for_branches_discard_type_var() {
+    check_no_mismatches(
+        r#"
+fn test() {
+    let f = || {
+        if true {
+            &""
+        } else {
+            ""
+        }
+    };
+}
+"#,
+    );
+}
+
 #[test]
 fn match_first_coerce() {
     check_no_mismatches(
@@ -182,6 +199,22 @@ fn test() {
     );
 }
 
+#[test]
+fn match_adjust_for_branches_discard_type_var() {
+    check_no_mismatches(
+        r#"
+fn test() {
+    let f = || {
+        match 0i32 {
+            0i32 => &"",
+            _ => "",
+        }
+    };
+}
+"#,
+    );
+}
+
 #[test]
 fn return_coerce_unknown() {
     check_types(
@@ -357,7 +390,7 @@ fn test() {
     let f: fn(u32) -> isize = foo;
                            // ^^^ adjustments: Pointer(ReifyFnPointer)
     let f: unsafe fn(u32) -> isize = foo;
-                                  // ^^^ adjustments: Pointer(ReifyFnPointer)
+                                  // ^^^ adjustments: Pointer(ReifyFnPointer), Pointer(UnsafeFnPointer)
 }",
     );
 }
@@ -388,7 +421,10 @@ fn coerce_closure_to_fn_ptr() {
     check_no_mismatches(
         r"
 fn test() {
-    let f: fn(u32) -> isize = |x| { 1 };
+    let f: fn(u32) -> u32 = |x| x;
+                         // ^^^^^ adjustments: Pointer(ClosureFnPointer(Safe))
+    let f: unsafe fn(u32) -> u32 = |x| x;
+                                // ^^^^^ adjustments: Pointer(ClosureFnPointer(Unsafe))
 }",
     );
 }
diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs
index f5324208c9a4e..cbd9bf32a5486 100644
--- a/src/tools/rust-analyzer/crates/hir/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs
@@ -2995,7 +2995,17 @@ impl Type {
         let callee = match self.ty.kind(Interner) {
             TyKind::Closure(id, _) => Callee::Closure(*id),
             TyKind::Function(_) => Callee::FnPtr,
-            _ => Callee::Def(self.ty.callable_def(db)?),
+            TyKind::FnDef(..) => Callee::Def(self.ty.callable_def(db)?),
+            _ => {
+                let ty = hir_ty::replace_errors_with_variables(&self.ty);
+                let sig = hir_ty::callable_sig_from_fnonce(&ty, self.env.clone(), db)?;
+                return Some(Callable {
+                    ty: self.clone(),
+                    sig,
+                    callee: Callee::Other,
+                    is_bound_method: false,
+                });
+            }
         };
 
         let sig = self.ty.callable_sig(db)?;
@@ -3464,6 +3474,7 @@ enum Callee {
     Def(CallableDefId),
     Closure(ClosureId),
     FnPtr,
+    Other,
 }
 
 pub enum CallableKind {
@@ -3472,6 +3483,8 @@ pub enum CallableKind {
     TupleEnumVariant(Variant),
     Closure,
     FnPtr,
+    /// Some other type that implements `FnOnce`.
+    Other,
 }
 
 impl Callable {
@@ -3483,6 +3496,7 @@ impl Callable {
             Def(CallableDefId::EnumVariantId(it)) => CallableKind::TupleEnumVariant(it.into()),
             Closure(_) => CallableKind::Closure,
             FnPtr => CallableKind::FnPtr,
+            Other => CallableKind::Other,
         }
     }
     pub fn receiver_param(&self, db: &dyn HirDatabase) -> Option {
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/assist_config.rs b/src/tools/rust-analyzer/crates/ide-assists/src/assist_config.rs
index 60d1588a44e54..b273ebc85a506 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/assist_config.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/assist_config.rs
@@ -14,4 +14,5 @@ pub struct AssistConfig {
     pub allowed: Option>,
     pub insert_use: InsertUseConfig,
     pub prefer_no_std: bool,
+    pub assist_emit_must_use: bool,
 }
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_explicit_type.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_explicit_type.rs
index bfa9759ec84be..b5f99726fe1c8 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_explicit_type.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_explicit_type.rs
@@ -69,14 +69,14 @@ pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> O
     let inferred_type = ty.display_source_code(ctx.db(), module.into()).ok()?;
     acc.add(
         AssistId("add_explicit_type", AssistKind::RefactorRewrite),
-        format!("Insert explicit type `{}`", inferred_type),
+        format!("Insert explicit type `{inferred_type}`"),
         pat_range,
         |builder| match ascribed_ty {
             Some(ascribed_ty) => {
                 builder.replace(ascribed_ty.syntax().text_range(), inferred_type);
             }
             None => {
-                builder.insert(pat_range.end(), format!(": {}", inferred_type));
+                builder.insert(pat_range.end(), format!(": {inferred_type}"));
             }
         },
     )
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_return_type.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_return_type.rs
index f858d7a15c242..89040a8569e63 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_return_type.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_return_type.rs
@@ -35,16 +35,16 @@ pub(crate) fn add_return_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt
             match builder_edit_pos {
                 InsertOrReplace::Insert(insert_pos, needs_whitespace) => {
                     let preceeding_whitespace = if needs_whitespace { " " } else { "" };
-                    builder.insert(insert_pos, &format!("{}-> {} ", preceeding_whitespace, ty))
+                    builder.insert(insert_pos, &format!("{preceeding_whitespace}-> {ty} "))
                 }
                 InsertOrReplace::Replace(text_range) => {
-                    builder.replace(text_range, &format!("-> {}", ty))
+                    builder.replace(text_range, &format!("-> {ty}"))
                 }
             }
             if let FnType::Closure { wrap_expr: true } = fn_type {
                 cov_mark::hit!(wrap_closure_non_block_expr);
                 // `|x| x` becomes `|x| -> T x` which is invalid, so wrap it in a block
-                builder.replace(tail_expr.syntax().text_range(), &format!("{{{}}}", tail_expr));
+                builder.replace(tail_expr.syntax().text_range(), &format!("{{{tail_expr}}}"));
             }
         },
     )
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_turbo_fish.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_turbo_fish.rs
index c0bf238db7317..acf82e4b25794 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_turbo_fish.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_turbo_fish.rs
@@ -93,12 +93,13 @@ pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
             builder.trigger_signature_help();
             match ctx.config.snippet_cap {
                 Some(cap) => {
-                    let snip = format!("::<{}>", get_snippet_fish_head(number_of_arguments));
+                    let fish_head = get_snippet_fish_head(number_of_arguments);
+                    let snip = format!("::<{fish_head}>");
                     builder.insert_snippet(cap, ident.text_range().end(), snip)
                 }
                 None => {
                     let fish_head = std::iter::repeat("_").take(number_of_arguments).format(", ");
-                    let snip = format!("::<{}>", fish_head);
+                    let snip = format!("::<{fish_head}>");
                     builder.insert(ident.text_range().end(), snip);
                 }
             }
@@ -109,7 +110,7 @@ pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
 /// This will create a snippet string with tabstops marked
 fn get_snippet_fish_head(number_of_arguments: usize) -> String {
     let mut fish_head = (1..number_of_arguments)
-        .format_with("", |i, f| f(&format_args!("${{{}:_}}, ", i)))
+        .format_with("", |i, f| f(&format_args!("${{{i}:_}}, ")))
         .to_string();
 
     // tabstop 0 is a special case and always the last one
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs
index 2853d1d1be3cd..57cfa17cc8e13 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs
@@ -123,20 +123,20 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
                     let lhs_range = lhs.syntax().text_range();
                     let not_lhs = invert_boolean_expression(lhs);
 
-                    edit.replace(lhs_range, format!("!({}", not_lhs.syntax().text()));
+                    edit.replace(lhs_range, format!("!({not_lhs}"));
                 }
 
                 if let Some(rhs) = terms.pop_back() {
                     let rhs_range = rhs.syntax().text_range();
                     let not_rhs = invert_boolean_expression(rhs);
 
-                    edit.replace(rhs_range, format!("{})", not_rhs.syntax().text()));
+                    edit.replace(rhs_range, format!("{not_rhs})"));
                 }
 
                 for term in terms {
                     let term_range = term.syntax().text_range();
                     let not_term = invert_boolean_expression(term);
-                    edit.replace(term_range, not_term.syntax().text());
+                    edit.replace(term_range, not_term.to_string());
                 }
             }
         },
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs
index 678dc877d1381..a689270bc0915 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs
@@ -127,10 +127,12 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<
         .sort_by_key(|import| Reverse(relevance_score(ctx, import, current_module.as_ref())));
 
     for import in proposed_imports {
+        let import_path = import.import_path;
+
         acc.add_group(
             &group_label,
             AssistId("auto_import", AssistKind::QuickFix),
-            format!("Import `{}`", import.import_path),
+            format!("Import `{import_path}`"),
             range,
             |builder| {
                 let scope = match scope.clone() {
@@ -138,7 +140,7 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<
                     ImportScope::Module(it) => ImportScope::Module(builder.make_mut(it)),
                     ImportScope::Block(it) => ImportScope::Block(builder.make_mut(it)),
                 };
-                insert_use(&scope, mod_path_to_ast(&import.import_path), &ctx.config.insert_use);
+                insert_use(&scope, mod_path_to_ast(&import_path), &ctx.config.insert_use);
             },
         );
     }
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_comment_block.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_comment_block.rs
index f171dd81a811e..312cb65abd2a1 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_comment_block.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_comment_block.rs
@@ -54,16 +54,17 @@ fn block_to_line(acc: &mut Assists, comment: ast::Comment) -> Option<()> {
 
             let indent_spaces = indentation.to_string();
             let output = lines
-                .map(|l| l.trim_start_matches(&indent_spaces))
-                .map(|l| {
+                .map(|line| {
+                    let line = line.trim_start_matches(&indent_spaces);
+
                     // Don't introduce trailing whitespace
-                    if l.is_empty() {
+                    if line.is_empty() {
                         line_prefix.to_string()
                     } else {
-                        format!("{} {}", line_prefix, l.trim_start_matches(&indent_spaces))
+                        format!("{line_prefix} {line}")
                     }
                 })
-                .join(&format!("\n{}", indent_spaces));
+                .join(&format!("\n{indent_spaces}"));
 
             edit.replace(target, output)
         },
@@ -96,7 +97,7 @@ fn line_to_block(acc: &mut Assists, comment: ast::Comment) -> Option<()> {
             let block_prefix =
                 CommentKind { shape: CommentShape::Block, ..comment.kind() }.prefix();
 
-            let output = format!("{}\n{}\n{}*/", block_prefix, block_comment_body, indentation);
+            let output = format!("{block_prefix}\n{block_comment_body}\n{indentation}*/");
 
             edit.replace(target, output)
         },
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_integer_literal.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_integer_literal.rs
index 9060696cdc8e9..ff2195f7e6c4a 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_integer_literal.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_integer_literal.rs
@@ -32,19 +32,19 @@ pub(crate) fn convert_integer_literal(acc: &mut Assists, ctx: &AssistContext<'_>
         }
 
         let mut converted = match target_radix {
-            Radix::Binary => format!("0b{:b}", value),
-            Radix::Octal => format!("0o{:o}", value),
+            Radix::Binary => format!("0b{value:b}"),
+            Radix::Octal => format!("0o{value:o}"),
             Radix::Decimal => value.to_string(),
-            Radix::Hexadecimal => format!("0x{:X}", value),
+            Radix::Hexadecimal => format!("0x{value:X}"),
         };
 
-        let label = format!("Convert {} to {}{}", literal, converted, suffix.unwrap_or_default());
-
         // Appends the type suffix back into the new literal if it exists.
         if let Some(suffix) = suffix {
             converted.push_str(suffix);
         }
 
+        let label = format!("Convert {literal} to {converted}");
+
         acc.add_group(
             &group_id,
             AssistId("convert_integer_literal", AssistKind::RefactorInline),
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_into_to_from.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_into_to_from.rs
index 95d11abe8bc0f..872b52c98fff2 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_into_to_from.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_into_to_from.rs
@@ -86,9 +86,9 @@ pub(crate) fn convert_into_to_from(acc: &mut Assists, ctx: &AssistContext<'_>) -
         impl_.syntax().text_range(),
         |builder| {
             builder.replace(src_type.syntax().text_range(), dest_type.to_string());
-            builder.replace(ast_trait.syntax().text_range(), format!("From<{}>", src_type));
+            builder.replace(ast_trait.syntax().text_range(), format!("From<{src_type}>"));
             builder.replace(into_fn_return.syntax().text_range(), "-> Self");
-            builder.replace(into_fn_params.syntax().text_range(), format!("(val: {})", src_type));
+            builder.replace(into_fn_params.syntax().text_range(), format!("(val: {src_type})"));
             builder.replace(into_fn_name.syntax().text_range(), "from");
 
             for s in selfs {
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs
index 2cf370c090743..80eecf4a09868 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs
@@ -119,19 +119,19 @@ pub(crate) fn convert_for_loop_with_for_each(
             {
                 // We have either "for x in &col" and col implements a method called iter
                 //             or "for x in &mut col" and col implements a method called iter_mut
-                format_to!(buf, "{}.{}()", expr_behind_ref, method);
+                format_to!(buf, "{expr_behind_ref}.{method}()");
             } else if let ast::Expr::RangeExpr(..) = iterable {
                 // range expressions need to be parenthesized for the syntax to be correct
-                format_to!(buf, "({})", iterable);
+                format_to!(buf, "({iterable})");
             } else if impls_core_iter(&ctx.sema, &iterable) {
-                format_to!(buf, "{}", iterable);
+                format_to!(buf, "{iterable}");
             } else if let ast::Expr::RefExpr(_) = iterable {
-                format_to!(buf, "({}).into_iter()", iterable);
+                format_to!(buf, "({iterable}).into_iter()");
             } else {
-                format_to!(buf, "{}.into_iter()", iterable);
+                format_to!(buf, "{iterable}.into_iter()");
             }
 
-            format_to!(buf, ".for_each(|{}| {});", pat, body);
+            format_to!(buf, ".for_each(|{pat}| {body});");
 
             builder.replace(for_loop.syntax().text_range(), buf)
         },
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_let_else_to_match.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_let_else_to_match.rs
index 00095de257d5a..c82a3b5303259 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_let_else_to_match.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_let_else_to_match.rs
@@ -80,7 +80,7 @@ fn binders_to_str(binders: &[(Name, bool)], addmut: bool) -> String {
         .map(
             |(ident, ismut)| {
                 if *ismut && addmut {
-                    format!("mut {}", ident)
+                    format!("mut {ident}")
                 } else {
                     ident.to_string()
                 }
@@ -93,7 +93,7 @@ fn binders_to_str(binders: &[(Name, bool)], addmut: bool) -> String {
     } else if binders.len() == 1 {
         vars
     } else {
-        format!("({})", vars)
+        format!("({vars})")
     }
 }
 
@@ -153,7 +153,7 @@ pub(crate) fn convert_let_else_to_match(acc: &mut Assists, ctx: &AssistContext<'
 
             let only_expr = let_else_block.statements().next().is_none();
             let branch2 = match &let_else_block.tail_expr() {
-                Some(tail) if only_expr => format!("{},", tail.syntax().text()),
+                Some(tail) if only_expr => format!("{tail},"),
                 _ => let_else_block.syntax().text().to_string(),
             };
             let replace = if binders.is_empty() {
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs
new file mode 100644
index 0000000000000..5bf04a3ad3719
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs
@@ -0,0 +1,413 @@
+use ide_db::defs::{Definition, NameRefClass};
+use syntax::{
+    ast::{self, HasName},
+    ted, AstNode, SyntaxNode,
+};
+
+use crate::{
+    assist_context::{AssistContext, Assists},
+    AssistId, AssistKind,
+};
+
+// Assist: convert_match_to_let_else
+//
+// Converts let statement with match initializer to let-else statement.
+//
+// ```
+// # //- minicore: option
+// fn foo(opt: Option<()>) {
+//     let val = $0match opt {
+//         Some(it) => it,
+//         None => return,
+//     };
+// }
+// ```
+// ->
+// ```
+// fn foo(opt: Option<()>) {
+//     let Some(val) = opt else { return };
+// }
+// ```
+pub(crate) fn convert_match_to_let_else(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+    let let_stmt: ast::LetStmt = ctx.find_node_at_offset()?;
+    let binding = find_binding(let_stmt.pat()?)?;
+
+    let initializer = match let_stmt.initializer() {
+        Some(ast::Expr::MatchExpr(it)) => it,
+        _ => return None,
+    };
+    let initializer_expr = initializer.expr()?;
+
+    let (extracting_arm, diverging_arm) = match find_arms(ctx, &initializer) {
+        Some(it) => it,
+        None => return None,
+    };
+    if extracting_arm.guard().is_some() {
+        cov_mark::hit!(extracting_arm_has_guard);
+        return None;
+    }
+
+    let diverging_arm_expr = diverging_arm.expr()?;
+    let extracting_arm_pat = extracting_arm.pat()?;
+    let extracted_variable = find_extracted_variable(ctx, &extracting_arm)?;
+
+    acc.add(
+        AssistId("convert_match_to_let_else", AssistKind::RefactorRewrite),
+        "Convert match to let-else",
+        let_stmt.syntax().text_range(),
+        |builder| {
+            let extracting_arm_pat = rename_variable(&extracting_arm_pat, extracted_variable, binding);
+            builder.replace(
+                let_stmt.syntax().text_range(),
+                format!("let {extracting_arm_pat} = {initializer_expr} else {{ {diverging_arm_expr} }};")
+            )
+        },
+    )
+}
+
+// Given a pattern, find the name introduced to the surrounding scope.
+fn find_binding(pat: ast::Pat) -> Option {
+    if let ast::Pat::IdentPat(ident) = pat {
+        Some(ident)
+    } else {
+        None
+    }
+}
+
+// Given a match expression, find extracting and diverging arms.
+fn find_arms(
+    ctx: &AssistContext<'_>,
+    match_expr: &ast::MatchExpr,
+) -> Option<(ast::MatchArm, ast::MatchArm)> {
+    let arms = match_expr.match_arm_list()?.arms().collect::>();
+    if arms.len() != 2 {
+        return None;
+    }
+
+    let mut extracting = None;
+    let mut diverging = None;
+    for arm in arms {
+        if ctx.sema.type_of_expr(&arm.expr().unwrap()).unwrap().original().is_never() {
+            diverging = Some(arm);
+        } else {
+            extracting = Some(arm);
+        }
+    }
+
+    match (extracting, diverging) {
+        (Some(extracting), Some(diverging)) => Some((extracting, diverging)),
+        _ => {
+            cov_mark::hit!(non_diverging_match);
+            None
+        }
+    }
+}
+
+// Given an extracting arm, find the extracted variable.
+fn find_extracted_variable(ctx: &AssistContext<'_>, arm: &ast::MatchArm) -> Option {
+    match arm.expr()? {
+        ast::Expr::PathExpr(path) => {
+            let name_ref = path.syntax().descendants().find_map(ast::NameRef::cast)?;
+            match NameRefClass::classify(&ctx.sema, &name_ref)? {
+                NameRefClass::Definition(Definition::Local(local)) => {
+                    let source = local.source(ctx.db()).value.left()?;
+                    Some(source.name()?)
+                }
+                _ => None,
+            }
+        }
+        _ => {
+            cov_mark::hit!(extracting_arm_is_not_an_identity_expr);
+            return None;
+        }
+    }
+}
+
+// Rename `extracted` with `binding` in `pat`.
+fn rename_variable(pat: &ast::Pat, extracted: ast::Name, binding: ast::IdentPat) -> SyntaxNode {
+    let syntax = pat.syntax().clone_for_update();
+    let extracted_syntax = syntax.covering_element(extracted.syntax().text_range());
+
+    // If `extracted` variable is a record field, we should rename it to `binding`,
+    // otherwise we just need to replace `extracted` with `binding`.
+
+    if let Some(record_pat_field) = extracted_syntax.ancestors().find_map(ast::RecordPatField::cast)
+    {
+        if let Some(name_ref) = record_pat_field.field_name() {
+            ted::replace(
+                record_pat_field.syntax(),
+                ast::make::record_pat_field(ast::make::name_ref(&name_ref.text()), binding.into())
+                    .syntax()
+                    .clone_for_update(),
+            );
+        }
+    } else {
+        ted::replace(extracted_syntax, binding.syntax().clone_for_update());
+    }
+
+    syntax
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::tests::{check_assist, check_assist_not_applicable};
+
+    use super::*;
+
+    #[test]
+    fn should_not_be_applicable_for_non_diverging_match() {
+        cov_mark::check!(non_diverging_match);
+        check_assist_not_applicable(
+            convert_match_to_let_else,
+            r#"
+//- minicore: option
+fn foo(opt: Option<()>) {
+    let val = $0match opt {
+        Some(it) => it,
+        None => (),
+    };
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn should_not_be_applicable_if_extracting_arm_is_not_an_identity_expr() {
+        cov_mark::check_count!(extracting_arm_is_not_an_identity_expr, 2);
+        check_assist_not_applicable(
+            convert_match_to_let_else,
+            r#"
+//- minicore: option
+fn foo(opt: Option) {
+    let val = $0match opt {
+        Some(it) => it + 1,
+        None => return,
+    };
+}
+"#,
+        );
+
+        check_assist_not_applicable(
+            convert_match_to_let_else,
+            r#"
+//- minicore: option
+fn foo(opt: Option<()>) {
+    let val = $0match opt {
+        Some(it) => {
+            let _ = 1 + 1;
+            it
+        },
+        None => return,
+    };
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn should_not_be_applicable_if_extracting_arm_has_guard() {
+        cov_mark::check!(extracting_arm_has_guard);
+        check_assist_not_applicable(
+            convert_match_to_let_else,
+            r#"
+//- minicore: option
+fn foo(opt: Option<()>) {
+    let val = $0match opt {
+        Some(it) if 2 > 1 => it,
+        None => return,
+    };
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn basic_pattern() {
+        check_assist(
+            convert_match_to_let_else,
+            r#"
+//- minicore: option
+fn foo(opt: Option<()>) {
+    let val = $0match opt {
+        Some(it) => it,
+        None => return,
+    };
+}
+    "#,
+            r#"
+fn foo(opt: Option<()>) {
+    let Some(val) = opt else { return };
+}
+    "#,
+        );
+    }
+
+    #[test]
+    fn keeps_modifiers() {
+        check_assist(
+            convert_match_to_let_else,
+            r#"
+//- minicore: option
+fn foo(opt: Option<()>) {
+    let ref mut val = $0match opt {
+        Some(it) => it,
+        None => return,
+    };
+}
+    "#,
+            r#"
+fn foo(opt: Option<()>) {
+    let Some(ref mut val) = opt else { return };
+}
+    "#,
+        );
+    }
+
+    #[test]
+    fn nested_pattern() {
+        check_assist(
+            convert_match_to_let_else,
+            r#"
+//- minicore: option, result
+fn foo(opt: Option>) {
+    let val = $0match opt {
+        Some(Ok(it)) => it,
+        _ => return,
+    };
+}
+    "#,
+            r#"
+fn foo(opt: Option>) {
+    let Some(Ok(val)) = opt else { return };
+}
+    "#,
+        );
+    }
+
+    #[test]
+    fn works_with_any_diverging_block() {
+        check_assist(
+            convert_match_to_let_else,
+            r#"
+//- minicore: option
+fn foo(opt: Option<()>) {
+    loop {
+        let val = $0match opt {
+            Some(it) => it,
+            None => break,
+        };
+    }
+}
+    "#,
+            r#"
+fn foo(opt: Option<()>) {
+    loop {
+        let Some(val) = opt else { break };
+    }
+}
+    "#,
+        );
+
+        check_assist(
+            convert_match_to_let_else,
+            r#"
+//- minicore: option
+fn foo(opt: Option<()>) {
+    loop {
+        let val = $0match opt {
+            Some(it) => it,
+            None => continue,
+        };
+    }
+}
+    "#,
+            r#"
+fn foo(opt: Option<()>) {
+    loop {
+        let Some(val) = opt else { continue };
+    }
+}
+    "#,
+        );
+
+        check_assist(
+            convert_match_to_let_else,
+            r#"
+//- minicore: option
+fn panic() -> ! {}
+
+fn foo(opt: Option<()>) {
+    loop {
+        let val = $0match opt {
+            Some(it) => it,
+            None => panic(),
+        };
+    }
+}
+    "#,
+            r#"
+fn panic() -> ! {}
+
+fn foo(opt: Option<()>) {
+    loop {
+        let Some(val) = opt else { panic() };
+    }
+}
+    "#,
+        );
+    }
+
+    #[test]
+    fn struct_pattern() {
+        check_assist(
+            convert_match_to_let_else,
+            r#"
+//- minicore: option
+struct Point {
+    x: i32,
+    y: i32,
+}
+
+fn foo(opt: Option) {
+    let val = $0match opt {
+        Some(Point { x: 0, y }) => y,
+        _ => return,
+    };
+}
+    "#,
+            r#"
+struct Point {
+    x: i32,
+    y: i32,
+}
+
+fn foo(opt: Option) {
+    let Some(Point { x: 0, y: val }) = opt else { return };
+}
+    "#,
+        );
+    }
+
+    #[test]
+    fn renames_whole_binding() {
+        check_assist(
+            convert_match_to_let_else,
+            r#"
+//- minicore: option
+fn foo(opt: Option) -> Option {
+    let val = $0match opt {
+        it @ Some(42) => it,
+        _ => return None,
+    };
+    val
+}
+    "#,
+            r#"
+fn foo(opt: Option) -> Option {
+    let val @ Some(42) = opt else { return None };
+    val
+}
+    "#,
+        );
+    }
+}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs
index cb75619ced9c3..b97be34c5f7e4 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs
@@ -129,32 +129,15 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext<'
                 }
                 Some((path, bound_ident)) => {
                     // If-let.
-                    let match_expr = {
-                        let happy_arm = {
-                            let pat = make::tuple_struct_pat(
-                                path,
-                                once(make::ext::simple_ident_pat(make::name("it")).into()),
-                            );
-                            let expr = {
-                                let path = make::ext::ident_path("it");
-                                make::expr_path(path)
-                            };
-                            make::match_arm(once(pat.into()), None, expr)
-                        };
-
-                        let sad_arm = make::match_arm(
-                            // FIXME: would be cool to use `None` or `Err(_)` if appropriate
-                            once(make::wildcard_pat().into()),
-                            None,
-                            early_expression,
-                        );
-
-                        make::expr_match(cond_expr, make::match_arm_list(vec![happy_arm, sad_arm]))
-                    };
-
-                    let let_stmt = make::let_stmt(bound_ident, None, Some(match_expr));
-                    let let_stmt = let_stmt.indent(if_indent_level);
-                    let_stmt.syntax().clone_for_update()
+                    let pat = make::tuple_struct_pat(path, once(bound_ident));
+                    let let_else_stmt = make::let_else_stmt(
+                        pat.into(),
+                        None,
+                        cond_expr,
+                        ast::make::tail_only_block_expr(early_expression),
+                    );
+                    let let_else_stmt = let_else_stmt.indent(if_indent_level);
+                    let_else_stmt.syntax().clone_for_update()
                 }
             };
 
@@ -238,10 +221,7 @@ fn main(n: Option) {
             r#"
 fn main(n: Option) {
     bar();
-    let n = match n {
-        Some(it) => it,
-        _ => return,
-    };
+    let Some(n) = n else { return };
     foo(n);
 
     // comment
@@ -264,10 +244,7 @@ fn main() {
 "#,
             r#"
 fn main() {
-    let x = match Err(92) {
-        Ok(it) => it,
-        _ => return,
-    };
+    let Ok(x) = Err(92) else { return };
     foo(x);
 }
 "#,
@@ -292,10 +269,7 @@ fn main(n: Option) {
             r#"
 fn main(n: Option) {
     bar();
-    let n = match n {
-        Some(it) => it,
-        _ => return,
-    };
+    let Some(n) = n else { return };
     foo(n);
 
     // comment
@@ -323,10 +297,7 @@ fn main(n: Option) {
             r#"
 fn main(n: Option) {
     bar();
-    let mut n = match n {
-        Some(it) => it,
-        _ => return,
-    };
+    let Some(mut n) = n else { return };
     foo(n);
 
     // comment
@@ -354,10 +325,7 @@ fn main(n: Option<&str>) {
             r#"
 fn main(n: Option<&str>) {
     bar();
-    let ref n = match n {
-        Some(it) => it,
-        _ => return,
-    };
+    let Some(ref n) = n else { return };
     foo(n);
 
     // comment
@@ -412,10 +380,7 @@ fn main() {
             r#"
 fn main() {
     while true {
-        let n = match n {
-            Some(it) => it,
-            _ => continue,
-        };
+        let Some(n) = n else { continue };
         foo(n);
         bar();
     }
@@ -469,10 +434,7 @@ fn main() {
             r#"
 fn main() {
     loop {
-        let n = match n {
-            Some(it) => it,
-            _ => continue,
-        };
+        let Some(n) = n else { continue };
         foo(n);
         bar();
     }
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs
index d8f522708460e..92e091fca126c 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs
@@ -226,7 +226,13 @@ fn edit_field_references(
 }
 
 fn generate_names(fields: impl Iterator) -> Vec {
-    fields.enumerate().map(|(i, _)| ast::make::name(&format!("field{}", i + 1))).collect()
+    fields
+        .enumerate()
+        .map(|(i, _)| {
+            let idx = i + 1;
+            ast::make::name(&format!("field{idx}"))
+        })
+        .collect()
 }
 
 #[cfg(test)]
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_two_arm_bool_match_to_matches_macro.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_two_arm_bool_match_to_matches_macro.rs
index 54a7f480a4e46..b1b0f587cd33d 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_two_arm_bool_match_to_matches_macro.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_two_arm_bool_match_to_matches_macro.rs
@@ -58,16 +58,16 @@ pub(crate) fn convert_two_arm_bool_match_to_matches_macro(
         target_range,
         |builder| {
             let mut arm_str = String::new();
-            if let Some(ref pat) = first_arm.pat() {
+            if let Some(pat) = &first_arm.pat() {
                 arm_str += &pat.to_string();
             }
-            if let Some(ref guard) = first_arm.guard() {
-                arm_str += &format!(" {}", &guard.to_string());
+            if let Some(guard) = &first_arm.guard() {
+                arm_str += &format!(" {guard}");
             }
             if invert_matches {
-                builder.replace(target_range, format!("!matches!({}, {})", expr, arm_str));
+                builder.replace(target_range, format!("!matches!({expr}, {arm_str})"));
             } else {
-                builder.replace(target_range, format!("matches!({}, {})", expr, arm_str));
+                builder.replace(target_range, format!("matches!({expr}, {arm_str})"));
             }
         },
     )
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs
index dc581ff3bd2c7..31c2ce7c1b54a 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs
@@ -133,7 +133,7 @@ fn generate_name(
     _usages: &Option,
 ) -> String {
     // FIXME: detect if name already used
-    format!("_{}", index)
+    format!("_{index}")
 }
 
 enum RefType {
@@ -168,12 +168,12 @@ fn edit_tuple_assignment(
     let add_cursor = |text: &str| {
         // place cursor on first tuple item
         let first_tuple = &data.field_names[0];
-        text.replacen(first_tuple, &format!("$0{}", first_tuple), 1)
+        text.replacen(first_tuple, &format!("$0{first_tuple}"), 1)
     };
 
     // with sub_pattern: keep original tuple and add subpattern: `tup @ (_0, _1)`
     if in_sub_pattern {
-        let text = format!(" @ {}", tuple_pat);
+        let text = format!(" @ {tuple_pat}");
         match ctx.config.snippet_cap {
             Some(cap) => {
                 let snip = add_cursor(&text);
@@ -314,9 +314,9 @@ struct RefData {
 impl RefData {
     fn format(&self, field_name: &str) -> String {
         match (self.needs_deref, self.needs_parentheses) {
-            (true, true) => format!("(*{})", field_name),
-            (true, false) => format!("*{}", field_name),
-            (false, true) => format!("({})", field_name),
+            (true, true) => format!("(*{field_name})"),
+            (true, false) => format!("*{field_name}"),
+            (false, true) => format!("({field_name})"),
             (false, false) => field_name.to_string(),
         }
     }
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs
index d6c8ea785f84a..0605883584922 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs
@@ -181,7 +181,7 @@ fn make_function_name(semantics_scope: &hir::SemanticsScope<'_>) -> ast::NameRef
     let mut counter = 0;
     while names_in_scope.contains(&name) {
         counter += 1;
-        name = format!("{}{}", &default_name, counter)
+        name = format!("{default_name}{counter}")
     }
     make::name_ref(&name)
 }
@@ -1291,19 +1291,23 @@ fn make_call(ctx: &AssistContext<'_>, fun: &Function, indent: IndentLevel) -> St
     match fun.outliving_locals.as_slice() {
         [] => {}
         [var] => {
-            format_to!(buf, "let {}{} = ", mut_modifier(var), var.local.name(ctx.db()))
+            let modifier = mut_modifier(var);
+            let name = var.local.name(ctx.db());
+            format_to!(buf, "let {modifier}{name} = ")
         }
         vars => {
             buf.push_str("let (");
             let bindings = vars.iter().format_with(", ", |local, f| {
-                f(&format_args!("{}{}", mut_modifier(local), local.local.name(ctx.db())))
+                let modifier = mut_modifier(local);
+                let name = local.local.name(ctx.db());
+                f(&format_args!("{modifier}{name}"))
             });
-            format_to!(buf, "{}", bindings);
+            format_to!(buf, "{bindings}");
             buf.push_str(") = ");
         }
     }
 
-    format_to!(buf, "{}", expr);
+    format_to!(buf, "{expr}");
     let insert_comma = fun
         .body
         .parent()
@@ -1447,6 +1451,8 @@ fn format_function(
     new_indent: IndentLevel,
 ) -> String {
     let mut fn_def = String::new();
+
+    let fun_name = &fun.name;
     let params = fun.make_param_list(ctx, module);
     let ret_ty = fun.make_ret_ty(ctx, module);
     let body = make_body(ctx, old_indent, new_indent, fun);
@@ -1454,42 +1460,28 @@ fn format_function(
     let async_kw = if fun.control_flow.is_async { "async " } else { "" };
     let unsafe_kw = if fun.control_flow.is_unsafe { "unsafe " } else { "" };
     let (generic_params, where_clause) = make_generic_params_and_where_clause(ctx, fun);
+
+    format_to!(fn_def, "\n\n{new_indent}{const_kw}{async_kw}{unsafe_kw}");
     match ctx.config.snippet_cap {
-        Some(_) => format_to!(
-            fn_def,
-            "\n\n{}{}{}{}fn $0{}",
-            new_indent,
-            const_kw,
-            async_kw,
-            unsafe_kw,
-            fun.name,
-        ),
-        None => format_to!(
-            fn_def,
-            "\n\n{}{}{}{}fn {}",
-            new_indent,
-            const_kw,
-            async_kw,
-            unsafe_kw,
-            fun.name,
-        ),
+        Some(_) => format_to!(fn_def, "fn $0{fun_name}"),
+        None => format_to!(fn_def, "fn {fun_name}"),
     }
 
     if let Some(generic_params) = generic_params {
-        format_to!(fn_def, "{}", generic_params);
+        format_to!(fn_def, "{generic_params}");
     }
 
-    format_to!(fn_def, "{}", params);
+    format_to!(fn_def, "{params}");
 
     if let Some(ret_ty) = ret_ty {
-        format_to!(fn_def, " {}", ret_ty);
+        format_to!(fn_def, " {ret_ty}");
     }
 
     if let Some(where_clause) = where_clause {
-        format_to!(fn_def, " {}", where_clause);
+        format_to!(fn_def, " {where_clause}");
     }
 
-    format_to!(fn_def, " {}", body);
+    format_to!(fn_def, " {body}");
 
     fn_def
 }
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs
index 897980c665049..56834394aebaa 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs
@@ -127,7 +127,7 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
             for item in items_to_be_processed {
                 let item = item.indent(IndentLevel(1));
                 let mut indented_item = String::new();
-                format_to!(indented_item, "{}{}", new_item_indent, item.to_string());
+                format_to!(indented_item, "{new_item_indent}{item}");
                 body_items.push(indented_item);
             }
 
@@ -137,30 +137,28 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
                 let mut impl_body_def = String::new();
 
                 if let Some(self_ty) = impl_.self_ty() {
-                    format_to!(
-                        impl_body_def,
-                        "{}impl {} {{\n{}\n{}}}",
-                        old_item_indent + 1,
-                        self_ty.to_string(),
-                        body,
-                        old_item_indent + 1
-                    );
-
+                    {
+                        let impl_indent = old_item_indent + 1;
+                        format_to!(
+                            impl_body_def,
+                            "{impl_indent}impl {self_ty} {{\n{body}\n{impl_indent}}}",
+                        );
+                    }
                     body = impl_body_def;
 
                     // Add the import for enum/struct corresponding to given impl block
                     module.make_use_stmt_of_node_with_super(self_ty.syntax());
                     for item in module.use_items {
-                        let mut indented_item = String::new();
-                        format_to!(indented_item, "{}{}", old_item_indent + 1, item.to_string());
-                        body = format!("{}\n\n{}", indented_item, body);
+                        let item_indent = old_item_indent + 1;
+                        body = format!("{item_indent}{item}\n\n{body}");
                     }
                 }
             }
 
             let mut module_def = String::new();
 
-            format_to!(module_def, "mod {} {{\n{}\n{}}}", module.name, body, old_item_indent);
+            let module_name = module.name;
+            format_to!(module_def, "mod {module_name} {{\n{body}\n{old_item_indent}}}");
 
             let mut usages_to_be_updated_for_curr_file = vec![];
             for usages_to_be_updated_for_file in usages_to_be_processed {
@@ -199,7 +197,7 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
                     builder.delete(range);
                 }
 
-                builder.insert(impl_.syntax().text_range().end(), format!("\n\n{}", module_def));
+                builder.insert(impl_.syntax().text_range().end(), format!("\n\n{module_def}"));
             } else {
                 builder.replace(module.text_range, module_def)
             }
@@ -343,9 +341,10 @@ impl Module {
                 && !self.text_range.contains_range(desc.text_range())
             {
                 if let Some(name_ref) = ast::NameRef::cast(desc) {
+                    let mod_name = self.name;
                     return Some((
                         name_ref.syntax().text_range(),
-                        format!("{}::{}", self.name, name_ref),
+                        format!("{mod_name}::{name_ref}"),
                     ));
                 }
             }
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs
index 970e948dfd930..b4e10667b07ab 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs
@@ -296,10 +296,14 @@ fn create_struct_def(
 
 fn update_variant(variant: &ast::Variant, generics: Option) -> Option<()> {
     let name = variant.name()?;
-    let ty = generics
+    let generic_args = generics
         .filter(|generics| generics.generic_params().count() > 0)
-        .map(|generics| make::ty(&format!("{}{}", &name.text(), generics.to_generic_args())))
-        .unwrap_or_else(|| make::ty(&name.text()));
+        .map(|generics| generics.to_generic_args());
+    // FIXME: replace with a `ast::make` constructor
+    let ty = match generic_args {
+        Some(generic_args) => make::ty(&format!("{name}{generic_args}")),
+        None => make::ty(&name.text()),
+    };
 
     // change from a record to a tuple field list
     let tuple_field = make::tuple_field(None, ty);
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs
index 03aa8601d14e1..3116935fc5e75 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs
@@ -1,8 +1,7 @@
 use either::Either;
 use ide_db::syntax_helpers::node_ext::walk_ty;
-use itertools::Itertools;
 use syntax::{
-    ast::{self, edit::IndentLevel, AstNode, HasGenericParams, HasName},
+    ast::{self, edit::IndentLevel, make, AstNode, HasGenericParams, HasName},
     match_ast,
 };
 
@@ -64,41 +63,29 @@ pub(crate) fn extract_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) ->
                 known_generics.extend(it.generic_params());
             }
             let generics = collect_used_generics(&ty, &known_generics);
+            let generic_params =
+                generics.map(|it| make::generic_param_list(it.into_iter().cloned()));
 
-            let replacement = if !generics.is_empty() {
-                format!(
-                    "Type<{}>",
-                    generics.iter().format_with(", ", |generic, f| {
-                        match generic {
-                            ast::GenericParam::ConstParam(cp) => f(&cp.name().unwrap()),
-                            ast::GenericParam::LifetimeParam(lp) => f(&lp.lifetime().unwrap()),
-                            ast::GenericParam::TypeParam(tp) => f(&tp.name().unwrap()),
-                        }
-                    })
-                )
-            } else {
-                String::from("Type")
-            };
+            let ty_args = generic_params
+                .as_ref()
+                .map_or(String::new(), |it| it.to_generic_args().to_string());
+            let replacement = format!("Type{ty_args}");
             builder.replace(target, replacement);
 
             let indent = IndentLevel::from_node(node);
-            let generics = if !generics.is_empty() {
-                format!("<{}>", generics.iter().format(", "))
-            } else {
-                String::new()
-            };
+            let generic_params = generic_params.map_or(String::new(), |it| it.to_string());
             match ctx.config.snippet_cap {
                 Some(cap) => {
                     builder.insert_snippet(
                         cap,
                         insert_pos,
-                        format!("type $0Type{} = {};\n\n{}", generics, ty, indent),
+                        format!("type $0Type{generic_params} = {ty};\n\n{indent}"),
                     );
                 }
                 None => {
                     builder.insert(
                         insert_pos,
-                        format!("type Type{} = {};\n\n{}", generics, ty, indent),
+                        format!("type Type{generic_params} = {ty};\n\n{indent}"),
                     );
                 }
             }
@@ -109,7 +96,7 @@ pub(crate) fn extract_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) ->
 fn collect_used_generics<'gp>(
     ty: &ast::Type,
     known_generics: &'gp [ast::GenericParam],
-) -> Vec<&'gp ast::GenericParam> {
+) -> Option> {
     // can't use a closure -> closure here cause lifetime inference fails for that
     fn find_lifetime(text: &str) -> impl Fn(&&ast::GenericParam) -> bool + '_ {
         move |gp: &&ast::GenericParam| match gp {
@@ -198,7 +185,8 @@ fn collect_used_generics<'gp>(
         ast::GenericParam::LifetimeParam(_) => 0,
         ast::GenericParam::TypeParam(_) => 1,
     });
-    generics
+
+    Some(generics).filter(|it| it.len() > 0)
 }
 
 #[cfg(test)]
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs
index 3596b6f82381b..a738deffb95b3 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs
@@ -91,13 +91,13 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
 
             match anchor {
                 Anchor::Before(_) | Anchor::Replace(_) => {
-                    format_to!(buf, "let {}{} = {}", var_modifier, var_name, reference_modifier)
+                    format_to!(buf, "let {var_modifier}{var_name} = {reference_modifier}")
                 }
                 Anchor::WrapInBlock(_) => {
-                    format_to!(buf, "{{ let {} = {}", var_name, reference_modifier)
+                    format_to!(buf, "{{ let {var_name} = {reference_modifier}")
                 }
             };
-            format_to!(buf, "{}", to_extract.syntax());
+            format_to!(buf, "{to_extract}");
 
             if let Anchor::Replace(stmt) = anchor {
                 cov_mark::hit!(test_extract_var_expr_stmt);
@@ -107,8 +107,8 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
                 match ctx.config.snippet_cap {
                     Some(cap) => {
                         let snip = buf.replace(
-                            &format!("let {}{}", var_modifier, var_name),
-                            &format!("let {}$0{}", var_modifier, var_name),
+                            &format!("let {var_modifier}{var_name}"),
+                            &format!("let {var_modifier}$0{var_name}"),
                         );
                         edit.replace_snippet(cap, expr_range, snip)
                     }
@@ -135,8 +135,8 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
             match ctx.config.snippet_cap {
                 Some(cap) => {
                     let snip = buf.replace(
-                        &format!("let {}{}", var_modifier, var_name),
-                        &format!("let {}$0{}", var_modifier, var_name),
+                        &format!("let {var_modifier}{var_name}"),
+                        &format!("let {var_modifier}$0{var_name}"),
                     );
                     edit.insert_snippet(cap, offset, snip)
                 }
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs
index b33846f546653..8764543028706 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs
@@ -57,8 +57,8 @@ fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext<'_>)
         if current_module.krate() == target_module.krate() { "pub(crate)" } else { "pub" };
 
     let assist_label = match target_name {
-        None => format!("Change visibility to {}", missing_visibility),
-        Some(name) => format!("Change visibility of {} to {}", name, missing_visibility),
+        None => format!("Change visibility to {missing_visibility}"),
+        Some(name) => format!("Change visibility of {name} to {missing_visibility}"),
     };
 
     acc.add(AssistId("fix_visibility", AssistKind::QuickFix), assist_label, target, |builder| {
@@ -68,15 +68,15 @@ fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext<'_>)
                 Some(current_visibility) => builder.replace_snippet(
                     cap,
                     current_visibility.syntax().text_range(),
-                    format!("$0{}", missing_visibility),
+                    format!("$0{missing_visibility}"),
                 ),
-                None => builder.insert_snippet(cap, offset, format!("$0{} ", missing_visibility)),
+                None => builder.insert_snippet(cap, offset, format!("$0{missing_visibility} ")),
             },
             None => match current_visibility {
                 Some(current_visibility) => {
                     builder.replace(current_visibility.syntax().text_range(), missing_visibility)
                 }
-                None => builder.insert(offset, format!("{} ", missing_visibility)),
+                None => builder.insert(offset, format!("{missing_visibility} ")),
             },
         }
     })
@@ -114,7 +114,7 @@ fn add_vis_to_referenced_record_field(acc: &mut Assists, ctx: &AssistContext<'_>
 
     let target_name = record_field_def.name(ctx.db());
     let assist_label =
-        format!("Change visibility of {}.{} to {}", parent_name, target_name, missing_visibility);
+        format!("Change visibility of {parent_name}.{target_name} to {missing_visibility}");
 
     acc.add(AssistId("fix_visibility", AssistKind::QuickFix), assist_label, target, |builder| {
         builder.edit_file(target_file);
@@ -123,15 +123,15 @@ fn add_vis_to_referenced_record_field(acc: &mut Assists, ctx: &AssistContext<'_>
                 Some(current_visibility) => builder.replace_snippet(
                     cap,
                     current_visibility.syntax().text_range(),
-                    format!("$0{}", missing_visibility),
+                    format!("$0{missing_visibility}"),
                 ),
-                None => builder.insert_snippet(cap, offset, format!("$0{} ", missing_visibility)),
+                None => builder.insert_snippet(cap, offset, format!("$0{missing_visibility} ")),
             },
             None => match current_visibility {
                 Some(current_visibility) => {
                     builder.replace(current_visibility.syntax().text_range(), missing_visibility)
                 }
-                None => builder.insert(offset, format!("{} ", missing_visibility)),
+                None => builder.insert(offset, format!("{missing_visibility} ")),
             },
         }
     })
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_projection_method.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_projection_method.rs
index bdd3cf4f06c25..c9aa41c845ad5 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_projection_method.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_projection_method.rs
@@ -124,6 +124,7 @@ fn generate_enum_projection_method(
         happy_case,
         sad_case,
     } = props;
+
     let variant = ctx.find_node_at_offset::()?;
     let variant_name = variant.name()?;
     let parent_enum = ast::Adt::Enum(variant.parent_enum());
@@ -144,7 +145,7 @@ fn generate_enum_projection_method(
         ast::StructKind::Unit => return None,
     };
 
-    let fn_name = format!("{}_{}", fn_name_prefix, &to_lower_snake_case(&variant_name.text()));
+    let fn_name = format!("{fn_name_prefix}_{}", &to_lower_snake_case(&variant_name.text()));
 
     // Return early if we've found an existing new fn
     let impl_def = find_struct_impl(ctx, &parent_enum, &[fn_name.clone()])?;
@@ -156,15 +157,25 @@ fn generate_enum_projection_method(
         assist_description,
         target,
         |builder| {
-            let vis = parent_enum.visibility().map_or(String::new(), |v| format!("{v} "));
+            let vis = parent_enum.visibility().map_or(String::new(), |v| format!("{} ", v));
+
+            let field_type_syntax = field_type.syntax();
+
+            let must_use = if ctx.config.assist_emit_must_use {
+                "#[must_use]\n    "
+            } else {
+                ""
+            };
+
             let method = format!(
-                "    {vis}fn {fn_name}({self_param}) -> {return_prefix}{field_type}{return_suffix} {{
+                "    {must_use}{vis}fn {fn_name}({self_param}) -> {return_prefix}{field_type_syntax}{return_suffix} {{
         if let Self::{variant_name}{pattern_suffix} = self {{
             {happy_case}({bound_name})
         }} else {{
             {sad_case}
         }}
-    }}");
+    }}"
+            );
 
             add_method_to_adt(builder, &parent_enum, impl_def, &method);
         },
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs
index 9f51cdaf8b1eb..0c546ce5d41c6 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs
@@ -1,3 +1,5 @@
+use std::collections::BTreeSet;
+
 use ast::make;
 use either::Either;
 use hir::{db::HirDatabase, PathResolution, Semantics, TypeInfo};
@@ -190,10 +192,10 @@ pub(crate) fn inline_call(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<
                 PathResolution::Def(hir::ModuleDef::Function(f)) => f,
                 _ => return None,
             };
-            (function, format!("Inline `{}`", path))
+            (function, format!("Inline `{path}`"))
         }
         ast::CallableExpr::MethodCall(call) => {
-            (ctx.sema.resolve_method_call(call)?, format!("Inline `{}`", name_ref))
+            (ctx.sema.resolve_method_call(call)?, format!("Inline `{name_ref}`"))
         }
     };
 
@@ -373,8 +375,44 @@ fn inline(
                 })
         }
     }
+
+    let mut func_let_vars: BTreeSet = BTreeSet::new();
+
+    // grab all of the local variable declarations in the function
+    for stmt in fn_body.statements() {
+        if let Some(let_stmt) = ast::LetStmt::cast(stmt.syntax().to_owned()) {
+            for has_token in let_stmt.syntax().children_with_tokens() {
+                if let Some(node) = has_token.as_node() {
+                    if let Some(ident_pat) = ast::IdentPat::cast(node.to_owned()) {
+                        func_let_vars.insert(ident_pat.syntax().text().to_string());
+                    }
+                }
+            }
+        }
+    }
+
     // Inline parameter expressions or generate `let` statements depending on whether inlining works or not.
     for ((pat, param_ty, _), usages, expr) in izip!(params, param_use_nodes, arguments).rev() {
+        // izip confuses RA due to our lack of hygiene info currently losing us type info causing incorrect errors
+        let usages: &[ast::PathExpr] = &*usages;
+        let expr: &ast::Expr = expr;
+
+        let insert_let_stmt = || {
+            let ty = sema.type_of_expr(expr).filter(TypeInfo::has_adjustment).and(param_ty.clone());
+            if let Some(stmt_list) = body.stmt_list() {
+                stmt_list.push_front(
+                    make::let_stmt(pat.clone(), ty, Some(expr.clone())).clone_for_update().into(),
+                )
+            }
+        };
+
+        // check if there is a local var in the function that conflicts with parameter
+        // if it does then emit a let statement and continue
+        if func_let_vars.contains(&expr.syntax().text().to_string()) {
+            insert_let_stmt();
+            continue;
+        }
+
         let inline_direct = |usage, replacement: &ast::Expr| {
             if let Some(field) = path_expr_as_record_field(usage) {
                 cov_mark::hit!(inline_call_inline_direct_field);
@@ -383,9 +421,7 @@ fn inline(
                 ted::replace(usage.syntax(), &replacement.syntax().clone_for_update());
             }
         };
-        // izip confuses RA due to our lack of hygiene info currently losing us type info causing incorrect errors
-        let usages: &[ast::PathExpr] = &*usages;
-        let expr: &ast::Expr = expr;
+
         match usages {
             // inline single use closure arguments
             [usage]
@@ -408,18 +444,11 @@ fn inline(
             }
             // can't inline, emit a let statement
             _ => {
-                let ty =
-                    sema.type_of_expr(expr).filter(TypeInfo::has_adjustment).and(param_ty.clone());
-                if let Some(stmt_list) = body.stmt_list() {
-                    stmt_list.push_front(
-                        make::let_stmt(pat.clone(), ty, Some(expr.clone()))
-                            .clone_for_update()
-                            .into(),
-                    )
-                }
+                insert_let_stmt();
             }
         }
     }
+
     if let Some(generic_arg_list) = generic_arg_list.clone() {
         if let Some((target, source)) = &sema.scope(node.syntax()).zip(sema.scope(fn_body.syntax()))
         {
@@ -1256,4 +1285,37 @@ impl A {
 "#,
         )
     }
+
+    #[test]
+    fn local_variable_shadowing_callers_argument() {
+        check_assist(
+            inline_call,
+            r#"
+fn foo(bar: u32, baz: u32) -> u32 {
+    let a = 1;
+    bar * baz * a * 6
+}
+fn main() {
+    let a = 7;
+    let b = 1;
+    let res = foo$0(a, b);
+}
+"#,
+            r#"
+fn foo(bar: u32, baz: u32) -> u32 {
+    let a = 1;
+    bar * baz * a * 6
+}
+fn main() {
+    let a = 7;
+    let b = 1;
+    let res = {
+        let bar = a;
+        let a = 1;
+        bar * b * a * 6
+    };
+}
+"#,
+        );
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_local_variable.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_local_variable.rs
index 7259d67819416..ce44100e34beb 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_local_variable.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_local_variable.rs
@@ -113,7 +113,7 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext<'_>)
         .collect::>>()?;
 
     let init_str = initializer_expr.syntax().text().to_string();
-    let init_in_paren = format!("({})", &init_str);
+    let init_in_paren = format!("({init_str})");
 
     let target = match target {
         ast::NameOrNameRef::Name(it) => it.syntax().text_range(),
@@ -132,7 +132,7 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext<'_>)
                 let replacement = if should_wrap { &init_in_paren } else { &init_str };
                 if ast::RecordExprField::for_field_name(&name).is_some() {
                     cov_mark::hit!(inline_field_shorthand);
-                    builder.insert(range.end(), format!(": {}", replacement));
+                    builder.insert(range.end(), format!(": {replacement}"));
                 } else {
                     builder.replace(range, replacement.clone())
                 }
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_lifetime.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_lifetime.rs
index 2fc754e3e50d1..a54dc4f96de00 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_lifetime.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_lifetime.rs
@@ -127,7 +127,7 @@ fn generate_unique_lifetime_param_name(
         Some(type_params) => {
             let used_lifetime_params: FxHashSet<_> =
                 type_params.lifetime_params().map(|p| p.syntax().text().to_string()).collect();
-            ('a'..='z').map(|it| format!("'{}", it)).find(|it| !used_lifetime_params.contains(it))
+            ('a'..='z').map(|it| format!("'{it}")).find(|it| !used_lifetime_params.contains(it))
         }
         None => Some("'a".to_string()),
     }
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_match_arms.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_match_arms.rs
index c24015b1c5175..641c90885bf53 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_match_arms.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_match_arms.rs
@@ -78,7 +78,7 @@ pub(crate) fn merge_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
                     .join(" | ")
             };
 
-            let arm = format!("{} => {},", pats, current_expr.syntax().text());
+            let arm = format!("{pats} => {current_expr},");
 
             if let [first, .., last] = &*arms_to_merge {
                 let start = first.syntax().text_range().start();
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_from_mod_rs.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_from_mod_rs.rs
index a6c85a2b18b34..1728c03cd03e2 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_from_mod_rs.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_from_mod_rs.rs
@@ -40,11 +40,11 @@ pub(crate) fn move_from_mod_rs(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
 
     let target = source_file.syntax().text_range();
     let module_name = module.name(ctx.db())?.to_string();
-    let path = format!("../{}.rs", module_name);
+    let path = format!("../{module_name}.rs");
     let dst = AnchoredPathBuf { anchor: ctx.file_id(), path };
     acc.add(
         AssistId("move_from_mod_rs", AssistKind::Refactor),
-        format!("Convert {}/mod.rs to {}.rs", module_name, module_name),
+        format!("Convert {module_name}/mod.rs to {module_name}.rs"),
         target,
         |builder| {
             builder.move_file(ctx.file_id(), dst);
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_guard.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_guard.rs
index b8f1b36deb93c..ec3281619cc3f 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_guard.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_guard.rs
@@ -133,16 +133,16 @@ pub(crate) fn move_arm_cond_to_match_guard(
             };
             let then_arm_end = match_arm.syntax().text_range().end();
             let indent_level = match_arm.indent_level();
-            let spaces = "    ".repeat(indent_level.0 as _);
+            let spaces = indent_level;
 
             let mut first = true;
             for (cond, block) in conds_blocks {
                 if !first {
-                    edit.insert(then_arm_end, format!("\n{}", spaces));
+                    edit.insert(then_arm_end, format!("\n{spaces}"));
                 } else {
                     first = false;
                 }
-                let guard = format!("{} if {} => ", match_pat, cond.syntax().text());
+                let guard = format!("{match_pat} if {cond} => ");
                 edit.insert(then_arm_end, guard);
                 let only_expr = block.statements().next().is_none();
                 match &block.tail_expr() {
@@ -158,7 +158,7 @@ pub(crate) fn move_arm_cond_to_match_guard(
             }
             if let Some(e) = tail {
                 cov_mark::hit!(move_guard_ifelse_else_tail);
-                let guard = format!("\n{}{} => ", spaces, match_pat);
+                let guard = format!("\n{spaces}{match_pat} => ");
                 edit.insert(then_arm_end, guard);
                 let only_expr = e.statements().next().is_none();
                 match &e.tail_expr() {
@@ -183,7 +183,7 @@ pub(crate) fn move_arm_cond_to_match_guard(
                     {
                         cov_mark::hit!(move_guard_ifelse_has_wildcard);
                     }
-                    _ => edit.insert(then_arm_end, format!("\n{}{} => {{}}", spaces, match_pat)),
+                    _ => edit.insert(then_arm_end, format!("\n{spaces}{match_pat} => {{}}")),
                 }
             }
         },
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_module_to_file.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_module_to_file.rs
index 7468318a594af..a7c605325ea69 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_module_to_file.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_module_to_file.rs
@@ -52,7 +52,7 @@ pub(crate) fn move_module_to_file(acc: &mut Assists, ctx: &AssistContext<'_>) ->
                 let mut buf = String::from("./");
                 match parent_module.name(ctx.db()) {
                     Some(name) if !parent_module.is_mod_rs(ctx.db()) => {
-                        format_to!(buf, "{}/", name)
+                        format_to!(buf, "{name}/")
                     }
                     _ => (),
                 }
@@ -82,7 +82,7 @@ pub(crate) fn move_module_to_file(acc: &mut Assists, ctx: &AssistContext<'_>) ->
                 items
             };
 
-            let buf = format!("mod {};", module_name);
+            let buf = format!("mod {module_name};");
 
             let replacement_start = match module_ast.mod_token() {
                 Some(mod_token) => mod_token.text_range(),
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_to_mod_rs.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_to_mod_rs.rs
index a909ce8b26791..076d25411a818 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_to_mod_rs.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_to_mod_rs.rs
@@ -40,11 +40,11 @@ pub(crate) fn move_to_mod_rs(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
 
     let target = source_file.syntax().text_range();
     let module_name = module.name(ctx.db())?.to_string();
-    let path = format!("./{}/mod.rs", module_name);
+    let path = format!("./{module_name}/mod.rs");
     let dst = AnchoredPathBuf { anchor: ctx.file_id(), path };
     acc.add(
         AssistId("move_to_mod_rs", AssistKind::Refactor),
-        format!("Convert {}.rs to {}/mod.rs", module_name, module_name),
+        format!("Convert {module_name}.rs to {module_name}/mod.rs"),
         target,
         |builder| {
             builder.move_file(ctx.file_id(), dst);
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/number_representation.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/number_representation.rs
index 424db7437a743..7e3fef516bfd8 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/number_representation.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/number_representation.rs
@@ -38,7 +38,7 @@ pub(crate) fn reformat_number_literal(acc: &mut Assists, ctx: &AssistContext<'_>
     converted.push_str(suffix);
 
     let group_id = GroupLabel("Reformat number literal".into());
-    let label = format!("Convert {} to {}", literal, converted);
+    let label = format!("Convert {literal} to {converted}");
     let range = literal.syntax().text_range();
     acc.add_group(
         &group_id,
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_method_call.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_method_call.rs
index e57d1d065d622..1ea87429c5092 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_method_call.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_method_call.rs
@@ -54,7 +54,7 @@ pub(crate) fn qualify_method_call(acc: &mut Assists, ctx: &AssistContext<'_>) ->
 
     acc.add(
         AssistId("qualify_method_call", AssistKind::RefactorInline),
-        format!("Qualify `{}` method call", ident.text()),
+        format!("Qualify `{ident}` method call"),
         range,
         |builder| {
             qualify_candidate.qualify(
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs
index 4b2af550bc5e4..e759e1561cbd0 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs
@@ -118,14 +118,14 @@ impl QualifyCandidate<'_> {
         match self {
             QualifyCandidate::QualifierStart(segment, generics) => {
                 let generics = generics.as_ref().map_or_else(String::new, ToString::to_string);
-                replacer(format!("{}{}::{}", import, generics, segment));
+                replacer(format!("{import}{generics}::{segment}"));
             }
             QualifyCandidate::UnqualifiedName(generics) => {
                 let generics = generics.as_ref().map_or_else(String::new, ToString::to_string);
-                replacer(format!("{}{}", import, generics));
+                replacer(format!("{import}{generics}"));
             }
             QualifyCandidate::TraitAssocItem(qualifier, segment) => {
-                replacer(format!("<{} as {}>::{}", qualifier, import, segment));
+                replacer(format!("<{qualifier} as {import}>::{segment}"));
             }
             QualifyCandidate::TraitMethod(db, mcall_expr) => {
                 Self::qualify_trait_method(db, mcall_expr, replacer, import, item);
@@ -155,16 +155,11 @@ impl QualifyCandidate<'_> {
                 hir::Access::Exclusive => make::expr_ref(receiver, true),
                 hir::Access::Owned => receiver,
             };
-            replacer(format!(
-                "{}::{}{}{}",
-                import,
-                method_name,
-                generics,
-                match arg_list {
-                    Some(args) => make::arg_list(iter::once(receiver).chain(args)),
-                    None => make::arg_list(iter::once(receiver)),
-                }
-            ));
+            let arg_list = match arg_list {
+                Some(args) => make::arg_list(iter::once(receiver).chain(args)),
+                None => make::arg_list(iter::once(receiver)),
+            };
+            replacer(format!("{import}::{method_name}{generics}{arg_list}"));
         }
         Some(())
     }
@@ -218,15 +213,17 @@ fn group_label(candidate: &ImportCandidate) -> GroupLabel {
         }
     }
     .text();
-    GroupLabel(format!("Qualify {}", name))
+    GroupLabel(format!("Qualify {name}"))
 }
 
 fn label(candidate: &ImportCandidate, import: &LocatedImport) -> String {
+    let import_path = &import.import_path;
+
     match candidate {
         ImportCandidate::Path(candidate) if candidate.qualifier.is_none() => {
-            format!("Qualify as `{}`", import.import_path)
+            format!("Qualify as `{import_path}`")
         }
-        _ => format!("Qualify with `{}`", import.import_path),
+        _ => format!("Qualify with `{import_path}`"),
     }
 }
 
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/raw_string.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/raw_string.rs
index dbe8cb7bf031f..c9bc25b27a5ed 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/raw_string.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/raw_string.rs
@@ -34,13 +34,10 @@ pub(crate) fn make_raw_string(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt
             let hashes = "#".repeat(required_hashes(&value).max(1));
             if matches!(value, Cow::Borrowed(_)) {
                 // Avoid replacing the whole string to better position the cursor.
-                edit.insert(token.syntax().text_range().start(), format!("r{}", hashes));
+                edit.insert(token.syntax().text_range().start(), format!("r{hashes}"));
                 edit.insert(token.syntax().text_range().end(), hashes);
             } else {
-                edit.replace(
-                    token.syntax().text_range(),
-                    format!("r{}\"{}\"{}", hashes, value, hashes),
-                );
+                edit.replace(token.syntax().text_range(), format!("r{hashes}\"{value}\"{hashes}"));
             }
         },
     )
@@ -83,7 +80,7 @@ pub(crate) fn make_usual_string(acc: &mut Assists, ctx: &AssistContext<'_>) -> O
                 }
             }
 
-            edit.replace(token.syntax().text_range(), format!("\"{}\"", escaped));
+            edit.replace(token.syntax().text_range(), format!("\"{escaped}\""));
         },
     )
 }
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs
index afaa7c933c739..3d9cbff177ba9 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs
@@ -102,7 +102,7 @@ pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<(
             };
             (
                 macro_call.syntax().text_range(),
-                if wrap { format!("({})", expr) } else { expr.to_string() },
+                if wrap { format!("({expr})") } else { expr.to_string() },
             )
         }
         // dbg!(expr0, expr1, ...)
@@ -127,8 +127,8 @@ mod tests {
     fn check(ra_fixture_before: &str, ra_fixture_after: &str) {
         check_assist(
             remove_dbg,
-            &format!("fn main() {{\n{}\n}}", ra_fixture_before),
-            &format!("fn main() {{\n{}\n}}", ra_fixture_after),
+            &format!("fn main() {{\n{ra_fixture_before}\n}}"),
+            &format!("fn main() {{\n{ra_fixture_after}\n}}"),
         );
     }
 
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs
index 9fd5e1886d206..f9ba289ee175f 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs
@@ -124,7 +124,7 @@ fn add_assist(
 ) -> Option<()> {
     let target = attr.syntax().text_range();
     let annotated_name = adt.name()?;
-    let label = format!("Convert to manual `impl {} for {}`", replace_trait_path, annotated_name);
+    let label = format!("Convert to manual `impl {replace_trait_path} for {annotated_name}`");
 
     acc.add(
         AssistId("replace_derive_with_manual_impl", AssistKind::Refactor),
@@ -158,11 +158,8 @@ fn add_assist(
                         }
                     }
 
-                    builder.insert_snippet(
-                        cap,
-                        insert_pos,
-                        format!("\n\n{}", render_snippet(cap, impl_def.syntax(), cursor)),
-                    )
+                    let rendered = render_snippet(cap, impl_def.syntax(), cursor);
+                    builder.insert_snippet(cap, insert_pos, format!("\n\n{rendered}"))
                 }
             };
         },
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_or_with_or_else.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_or_with_or_else.rs
index 7d91be6210136..77382056c1833 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_or_with_or_else.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_or_with_or_else.rs
@@ -62,7 +62,7 @@ pub(crate) fn replace_or_with_or_else(acc: &mut Assists, ctx: &AssistContext<'_>
 
     acc.add(
         AssistId("replace_or_with_or_else", AssistKind::RefactorRewrite),
-        format!("Replace {} with {}", name.text(), replace),
+        format!("Replace {name} with {replace}"),
         call.syntax().text_range(),
         |builder| {
             builder.replace(name.syntax().text_range(), replace);
@@ -138,7 +138,7 @@ pub(crate) fn replace_or_else_with_or(acc: &mut Assists, ctx: &AssistContext<'_>
 
     acc.add(
         AssistId("replace_or_else_with_or", AssistKind::RefactorRewrite),
-        format!("Replace {} with {}", name.text(), replace),
+        format!("Replace {name} with {replace}"),
         call.syntax().text_range(),
         |builder| {
             builder.replace(name.syntax().text_range(), replace);
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_turbofish_with_explicit_type.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_turbofish_with_explicit_type.rs
index 521447c26dfbe..c177adc7a10d7 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_turbofish_with_explicit_type.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_turbofish_with_explicit_type.rs
@@ -79,7 +79,7 @@ pub(crate) fn replace_turbofish_with_explicit_type(
             "Replace turbofish with explicit type",
             TextRange::new(initializer_start, turbofish_range.end()),
             |builder| {
-                builder.insert(ident_range.end(), format!(": {}", returned_type));
+                builder.insert(ident_range.end(), format!(": {returned_type}"));
                 builder.delete(turbofish_range);
             },
         );
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unnecessary_async.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unnecessary_async.rs
index d5cd2d551349d..0439883225330 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unnecessary_async.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unnecessary_async.rs
@@ -44,6 +44,12 @@ pub(crate) fn unnecessary_async(acc: &mut Assists, ctx: &AssistContext<'_>) -> O
     if function.body()?.syntax().descendants().find_map(ast::AwaitExpr::cast).is_some() {
         return None;
     }
+    // Do nothing if the method is a member of trait.
+    if let Some(impl_) = function.syntax().ancestors().nth(2).and_then(ast::Impl::cast) {
+        if let Some(_) = impl_.trait_() {
+            return None;
+        }
+    }
 
     // Remove the `async` keyword plus whitespace after it, if any.
     let async_range = {
@@ -254,4 +260,18 @@ pub async fn f(s: &S) { s.f2() }"#,
     fn does_not_apply_when_not_on_prototype() {
         check_assist_not_applicable(unnecessary_async, "pub async fn f() { $0f2() }")
     }
+
+    #[test]
+    fn does_not_apply_on_async_trait_method() {
+        check_assist_not_applicable(
+            unnecessary_async,
+            r#"
+trait Trait {
+    async fn foo();
+}
+impl Trait for () {
+    $0async fn foo() {}
+}"#,
+        );
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_tuple.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_tuple.rs
index 25c58d086e977..d09614c51127e 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_tuple.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_tuple.rs
@@ -69,13 +69,13 @@ pub(crate) fn unwrap_tuple(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option
                 for (pat, ty, expr) in
                     itertools::izip!(tuple_pat.fields(), tys.fields(), tuple_init.fields())
                 {
-                    zipped_decls.push_str(&format!("{}let {pat}: {ty} = {expr};\n", indents))
+                    zipped_decls.push_str(&format!("{indents}let {pat}: {ty} = {expr};\n"))
                 }
                 edit.replace(parent.text_range(), zipped_decls.trim());
             } else {
                 let mut zipped_decls = String::new();
                 for (pat, expr) in itertools::izip!(tuple_pat.fields(), tuple_init.fields()) {
-                    zipped_decls.push_str(&format!("{}let {pat} = {expr};\n", indents));
+                    zipped_decls.push_str(&format!("{indents}let {pat} = {expr};\n"));
                 }
                 edit.replace(parent.text_range(), zipped_decls.trim());
             }
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type_in_result.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type_in_result.rs
index 83446387db1cf..b6c489eb62eef 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type_in_result.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type_in_result.rs
@@ -76,11 +76,11 @@ pub(crate) fn wrap_return_type_in_result(acc: &mut Assists, ctx: &AssistContext<
 
             match ctx.config.snippet_cap {
                 Some(cap) => {
-                    let snippet = format!("Result<{}, ${{0:_}}>", type_ref);
+                    let snippet = format!("Result<{type_ref}, ${{0:_}}>");
                     builder.replace_snippet(cap, type_ref.syntax().text_range(), snippet)
                 }
                 None => builder
-                    .replace(type_ref.syntax().text_range(), format!("Result<{}, _>", type_ref)),
+                    .replace(type_ref.syntax().text_range(), format!("Result<{type_ref}, _>")),
             }
         },
     )
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs
index a07318cefad27..387cc63142825 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs
@@ -120,6 +120,7 @@ mod handlers {
     mod convert_into_to_from;
     mod convert_iter_for_each_to_for;
     mod convert_let_else_to_match;
+    mod convert_match_to_let_else;
     mod convert_tuple_struct_to_named_struct;
     mod convert_named_struct_to_tuple_struct;
     mod convert_to_guarded_return;
@@ -220,6 +221,7 @@ mod handlers {
             convert_iter_for_each_to_for::convert_for_loop_with_for_each,
             convert_let_else_to_match::convert_let_else_to_match,
             convert_named_struct_to_tuple_struct::convert_named_struct_to_tuple_struct,
+            convert_match_to_let_else::convert_match_to_let_else,
             convert_to_guarded_return::convert_to_guarded_return,
             convert_tuple_struct_to_named_struct::convert_tuple_struct_to_named_struct,
             convert_two_arm_bool_match_to_matches_macro::convert_two_arm_bool_match_to_matches_macro,
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs b/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs
index f7f2417d0745d..92ced27c78aed 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs
@@ -30,6 +30,7 @@ pub(crate) const TEST_CONFIG: AssistConfig = AssistConfig {
         skip_glob_imports: true,
     },
     prefer_no_std: false,
+    assist_emit_must_use: false,
 };
 
 pub(crate) fn with_single_file(text: &str) -> (RootDatabase, FileId) {
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs
index 2c4000efe0fa2..029d169899bb4 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs
@@ -407,6 +407,27 @@ fn main() {
     )
 }
 
+#[test]
+fn doctest_convert_match_to_let_else() {
+    check_doc_test(
+        "convert_match_to_let_else",
+        r#####"
+//- minicore: option
+fn foo(opt: Option<()>) {
+    let val = $0match opt {
+        Some(it) => it,
+        None => return,
+    };
+}
+"#####,
+        r#####"
+fn foo(opt: Option<()>) {
+    let Some(val) = opt else { return };
+}
+"#####,
+    )
+}
+
 #[test]
 fn doctest_convert_named_struct_to_tuple_struct() {
     check_doc_test(
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs
index db32e7182c44d..307e67927056b 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs
@@ -189,8 +189,8 @@ pub(crate) fn render_snippet(_cap: SnippetCap, node: &SyntaxNode, cursor: Cursor
     let mut placeholder = cursor.node().to_string();
     escape(&mut placeholder);
     let tab_stop = match cursor {
-        Cursor::Replace(placeholder) => format!("${{0:{}}}", placeholder),
-        Cursor::Before(placeholder) => format!("$0{}", placeholder),
+        Cursor::Replace(placeholder) => format!("${{0:{placeholder}}}"),
+        Cursor::Before(placeholder) => format!("$0{placeholder}"),
     };
 
     let mut buf = node.to_string();
@@ -539,17 +539,17 @@ impl ReferenceConversion {
             ReferenceConversionType::AsRefSlice => {
                 let type_argument_name =
                     self.ty.type_arguments().next().unwrap().display(db).to_string();
-                format!("&[{}]", type_argument_name)
+                format!("&[{type_argument_name}]")
             }
             ReferenceConversionType::Dereferenced => {
                 let type_argument_name =
                     self.ty.type_arguments().next().unwrap().display(db).to_string();
-                format!("&{}", type_argument_name)
+                format!("&{type_argument_name}")
             }
             ReferenceConversionType::Option => {
                 let type_argument_name =
                     self.ty.type_arguments().next().unwrap().display(db).to_string();
-                format!("Option<&{}>", type_argument_name)
+                format!("Option<&{type_argument_name}>")
             }
             ReferenceConversionType::Result => {
                 let mut type_arguments = self.ty.type_arguments();
@@ -557,19 +557,19 @@ impl ReferenceConversion {
                     type_arguments.next().unwrap().display(db).to_string();
                 let second_type_argument_name =
                     type_arguments.next().unwrap().display(db).to_string();
-                format!("Result<&{}, &{}>", first_type_argument_name, second_type_argument_name)
+                format!("Result<&{first_type_argument_name}, &{second_type_argument_name}>")
             }
         }
     }
 
     pub(crate) fn getter(&self, field_name: String) -> String {
         match self.conversion {
-            ReferenceConversionType::Copy => format!("self.{}", field_name),
+            ReferenceConversionType::Copy => format!("self.{field_name}"),
             ReferenceConversionType::AsRefStr
             | ReferenceConversionType::AsRefSlice
             | ReferenceConversionType::Dereferenced
             | ReferenceConversionType::Option
-            | ReferenceConversionType::Result => format!("self.{}.as_ref()", field_name),
+            | ReferenceConversionType::Result => format!("self.{field_name}.as_ref()"),
         }
     }
 }
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils/gen_trait_fn_body.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils/gen_trait_fn_body.rs
index 7a0c912959a12..6c87e66c134d7 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/utils/gen_trait_fn_body.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils/gen_trait_fn_body.rs
@@ -41,7 +41,7 @@ fn gen_clone_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
             let mut arms = vec![];
             for variant in list.variants() {
                 let name = variant.name()?;
-                let variant_name = make::ext::path_from_idents(["Self", &format!("{}", name)])?;
+                let variant_name = make::ext::path_from_idents(["Self", &format!("{name}")])?;
 
                 match variant.field_list() {
                     // => match self { Self::Name { x } => Self::Name { x: x.clone() } }
@@ -70,7 +70,7 @@ fn gen_clone_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
                         let mut pats = vec![];
                         let mut fields = vec![];
                         for (i, _) in list.fields().enumerate() {
-                            let field_name = format!("arg{}", i);
+                            let field_name = format!("arg{i}");
                             let pat = make::ident_pat(false, false, make::name(&field_name));
                             pats.push(pat.into());
 
@@ -118,7 +118,7 @@ fn gen_clone_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
                     let mut fields = vec![];
                     for (i, _) in field_list.fields().enumerate() {
                         let f_path = make::expr_path(make::ext::ident_path("self"));
-                        let target = make::expr_field(f_path, &format!("{}", i));
+                        let target = make::expr_field(f_path, &format!("{i}"));
                         fields.push(gen_clone_call(target));
                     }
                     let struct_name = make::expr_path(make::ext::ident_path("Self"));
@@ -151,7 +151,7 @@ fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
             let mut arms = vec![];
             for variant in list.variants() {
                 let name = variant.name()?;
-                let variant_name = make::ext::path_from_idents(["Self", &format!("{}", name)])?;
+                let variant_name = make::ext::path_from_idents(["Self", &format!("{name}")])?;
                 let target = make::expr_path(make::ext::ident_path("f"));
 
                 match variant.field_list() {
@@ -159,7 +159,7 @@ fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
                         // => f.debug_struct(name)
                         let target = make::expr_path(make::ext::ident_path("f"));
                         let method = make::name_ref("debug_struct");
-                        let struct_name = format!("\"{}\"", name);
+                        let struct_name = format!("\"{name}\"");
                         let args = make::arg_list(Some(make::expr_literal(&struct_name).into()));
                         let mut expr = make::expr_method_call(target, method, args);
 
@@ -173,8 +173,8 @@ fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
 
                             // => .field("field_name", field)
                             let method_name = make::name_ref("field");
-                            let name = make::expr_literal(&(format!("\"{}\"", field_name))).into();
-                            let path = &format!("{}", field_name);
+                            let name = make::expr_literal(&(format!("\"{field_name}\""))).into();
+                            let path = &format!("{field_name}");
                             let path = make::expr_path(make::ext::ident_path(path));
                             let args = make::arg_list(vec![name, path]);
                             expr = make::expr_method_call(expr, method_name, args);
@@ -192,13 +192,13 @@ fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
                         // => f.debug_tuple(name)
                         let target = make::expr_path(make::ext::ident_path("f"));
                         let method = make::name_ref("debug_tuple");
-                        let struct_name = format!("\"{}\"", name);
+                        let struct_name = format!("\"{name}\"");
                         let args = make::arg_list(Some(make::expr_literal(&struct_name).into()));
                         let mut expr = make::expr_method_call(target, method, args);
 
                         let mut pats = vec![];
                         for (i, _) in list.fields().enumerate() {
-                            let name = format!("arg{}", i);
+                            let name = format!("arg{i}");
 
                             // create a field pattern for use in `MyStruct(fields..)`
                             let field_name = make::name(&name);
@@ -222,7 +222,7 @@ fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
                         arms.push(make::match_arm(Some(pat.into()), None, expr));
                     }
                     None => {
-                        let fmt_string = make::expr_literal(&(format!("\"{}\"", name))).into();
+                        let fmt_string = make::expr_literal(&(format!("\"{name}\""))).into();
                         let args = make::arg_list([target, fmt_string]);
                         let macro_name = make::expr_path(make::ext::ident_path("write"));
                         let macro_call = make::expr_macro_call(macro_name, args);
@@ -244,7 +244,7 @@ fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
         }
 
         ast::Adt::Struct(strukt) => {
-            let name = format!("\"{}\"", annotated_name);
+            let name = format!("\"{annotated_name}\"");
             let args = make::arg_list(Some(make::expr_literal(&name).into()));
             let target = make::expr_path(make::ext::ident_path("f"));
 
@@ -258,10 +258,10 @@ fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
                     let mut expr = make::expr_method_call(target, method, args);
                     for field in field_list.fields() {
                         let name = field.name()?;
-                        let f_name = make::expr_literal(&(format!("\"{}\"", name))).into();
+                        let f_name = make::expr_literal(&(format!("\"{name}\""))).into();
                         let f_path = make::expr_path(make::ext::ident_path("self"));
                         let f_path = make::expr_ref(f_path, false);
-                        let f_path = make::expr_field(f_path, &format!("{}", name));
+                        let f_path = make::expr_field(f_path, &format!("{name}"));
                         let args = make::arg_list([f_name, f_path]);
                         expr = make::expr_method_call(expr, make::name_ref("field"), args);
                     }
@@ -275,7 +275,7 @@ fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
                     for (i, _) in field_list.fields().enumerate() {
                         let f_path = make::expr_path(make::ext::ident_path("self"));
                         let f_path = make::expr_ref(f_path, false);
-                        let f_path = make::expr_field(f_path, &format!("{}", i));
+                        let f_path = make::expr_field(f_path, &format!("{i}"));
                         let method = make::name_ref("field");
                         expr = make::expr_method_call(expr, method, make::arg_list(Some(f_path)));
                     }
@@ -379,7 +379,7 @@ fn gen_hash_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
                 let mut stmts = vec![];
                 for (i, _) in field_list.fields().enumerate() {
                     let base = make::expr_path(make::ext::ident_path("self"));
-                    let target = make::expr_field(base, &format!("{}", i));
+                    let target = make::expr_field(base, &format!("{i}"));
                     stmts.push(gen_hash_call(target));
                 }
                 make::block_expr(stmts, None).indent(ast::edit::IndentLevel(1))
@@ -453,10 +453,10 @@ fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
                         for field in list.fields() {
                             let field_name = field.name()?.to_string();
 
-                            let l_name = &format!("l_{}", field_name);
+                            let l_name = &format!("l_{field_name}");
                             l_fields.push(gen_record_pat_field(&field_name, l_name));
 
-                            let r_name = &format!("r_{}", field_name);
+                            let r_name = &format!("r_{field_name}");
                             r_fields.push(gen_record_pat_field(&field_name, r_name));
 
                             let lhs = make::expr_path(make::ext::ident_path(l_name));
@@ -484,12 +484,12 @@ fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
                         let mut r_fields = vec![];
 
                         for (i, _) in list.fields().enumerate() {
-                            let field_name = format!("{}", i);
+                            let field_name = format!("{i}");
 
-                            let l_name = format!("l{}", field_name);
+                            let l_name = format!("l{field_name}");
                             l_fields.push(gen_tuple_field(&l_name));
 
-                            let r_name = format!("r{}", field_name);
+                            let r_name = format!("r{field_name}");
                             r_fields.push(gen_tuple_field(&r_name));
 
                             let lhs = make::expr_path(make::ext::ident_path(&l_name));
@@ -548,7 +548,7 @@ fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
             Some(ast::FieldList::TupleFieldList(field_list)) => {
                 let mut expr = None;
                 for (i, _) in field_list.fields().enumerate() {
-                    let idx = format!("{}", i);
+                    let idx = format!("{i}");
                     let lhs = make::expr_path(make::ext::ident_path("self"));
                     let lhs = make::expr_field(lhs, &idx);
                     let rhs = make::expr_path(make::ext::ident_path("other"));
@@ -628,7 +628,7 @@ fn gen_partial_ord(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
             Some(ast::FieldList::TupleFieldList(field_list)) => {
                 let mut exprs = vec![];
                 for (i, _) in field_list.fields().enumerate() {
-                    let idx = format!("{}", i);
+                    let idx = format!("{i}");
                     let lhs = make::expr_path(make::ext::ident_path("self"));
                     let lhs = make::expr_field(lhs, &idx);
                     let rhs = make::expr_path(make::ext::ident_path("other"));
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs
index 9a891cea2d458..b9bd47f7da504 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs
@@ -69,10 +69,6 @@ pub(crate) fn complete_postfix(
         }
     }
 
-    if !ctx.config.snippets.is_empty() {
-        add_custom_postfix_completions(acc, ctx, &postfix_snippet, &receiver_text);
-    }
-
     let try_enum = TryEnum::from_ty(&ctx.sema, &receiver_ty.strip_references());
     if let Some(try_enum) = &try_enum {
         match try_enum {
@@ -140,6 +136,10 @@ pub(crate) fn complete_postfix(
         None => return,
     };
 
+    if !ctx.config.snippets.is_empty() {
+        add_custom_postfix_completions(acc, ctx, &postfix_snippet, &receiver_text);
+    }
+
     match try_enum {
         Some(try_enum) => match try_enum {
             TryEnum::Result => {
@@ -613,4 +613,25 @@ fn main() {
             r#"fn main() { log::error!("{}", 2+2) }"#,
         );
     }
+
+    #[test]
+    fn postfix_custom_snippets_completion_for_references() {
+        check_edit_with_config(
+            CompletionConfig {
+                snippets: vec![Snippet::new(
+                    &[],
+                    &["ok".into()],
+                    &["Ok(${receiver})".into()],
+                    "",
+                    &[],
+                    crate::SnippetScope::Expr,
+                )
+                .unwrap()],
+                ..TEST_CONFIG
+            },
+            "ok",
+            r#"fn main() { &&42.$0 }"#,
+            r#"fn main() { Ok(&&42) }"#,
+        );
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/search.rs b/src/tools/rust-analyzer/crates/ide-db/src/search.rs
index 82b85f2fa5edd..aa5d7e9beb54f 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/search.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/search.rs
@@ -446,33 +446,47 @@ impl<'a> FindUsages<'a> {
             })
         }
 
-        // FIXME: There should be optimization potential here
-        // Currently we try to descend everything we find which
-        // means we call `Semantics::descend_into_macros` on
-        // every textual hit. That function is notoriously
-        // expensive even for things that do not get down mapped
-        // into macros.
+        let find_nodes = move |name: &str, node: &syntax::SyntaxNode, offset: TextSize| {
+            node.token_at_offset(offset).find(|it| it.text() == name).map(|token| {
+                // FIXME: There should be optimization potential here
+                // Currently we try to descend everything we find which
+                // means we call `Semantics::descend_into_macros` on
+                // every textual hit. That function is notoriously
+                // expensive even for things that do not get down mapped
+                // into macros.
+                sema.descend_into_macros(token).into_iter().filter_map(|it| it.parent())
+            })
+        };
+
         for (text, file_id, search_range) in scope_files(sema, &search_scope) {
             let tree = Lazy::new(move || sema.parse(file_id).syntax().clone());
 
             // Search for occurrences of the items name
             for offset in match_indices(&text, finder, search_range) {
-                for name in sema.find_nodes_at_offset_with_descend(&tree, offset) {
-                    if match name {
-                        ast::NameLike::NameRef(name_ref) => self.found_name_ref(&name_ref, sink),
-                        ast::NameLike::Name(name) => self.found_name(&name, sink),
-                        ast::NameLike::Lifetime(lifetime) => self.found_lifetime(&lifetime, sink),
-                    } {
-                        return;
+                if let Some(iter) = find_nodes(name, &tree, offset) {
+                    for name in iter.filter_map(ast::NameLike::cast) {
+                        if match name {
+                            ast::NameLike::NameRef(name_ref) => {
+                                self.found_name_ref(&name_ref, sink)
+                            }
+                            ast::NameLike::Name(name) => self.found_name(&name, sink),
+                            ast::NameLike::Lifetime(lifetime) => {
+                                self.found_lifetime(&lifetime, sink)
+                            }
+                        } {
+                            return;
+                        }
                     }
                 }
             }
             // Search for occurrences of the `Self` referring to our type
             if let Some((self_ty, finder)) = &include_self_kw_refs {
                 for offset in match_indices(&text, finder, search_range) {
-                    for name_ref in sema.find_nodes_at_offset_with_descend(&tree, offset) {
-                        if self.found_self_ty_name_ref(self_ty, &name_ref, sink) {
-                            return;
+                    if let Some(iter) = find_nodes("Self", &tree, offset) {
+                        for name_ref in iter.filter_map(ast::NameRef::cast) {
+                            if self.found_self_ty_name_ref(self_ty, &name_ref, sink) {
+                                return;
+                            }
                         }
                     }
                 }
@@ -493,17 +507,21 @@ impl<'a> FindUsages<'a> {
                     let tree = Lazy::new(move || sema.parse(file_id).syntax().clone());
 
                     for offset in match_indices(&text, finder, search_range) {
-                        for name_ref in sema.find_nodes_at_offset_with_descend(&tree, offset) {
-                            if self.found_name_ref(&name_ref, sink) {
-                                return;
+                        if let Some(iter) = find_nodes("super", &tree, offset) {
+                            for name_ref in iter.filter_map(ast::NameRef::cast) {
+                                if self.found_name_ref(&name_ref, sink) {
+                                    return;
+                                }
                             }
                         }
                     }
                     if let Some(finder) = &is_crate_root {
                         for offset in match_indices(&text, finder, search_range) {
-                            for name_ref in sema.find_nodes_at_offset_with_descend(&tree, offset) {
-                                if self.found_name_ref(&name_ref, sink) {
-                                    return;
+                            if let Some(iter) = find_nodes("crate", &tree, offset) {
+                                for name_ref in iter.filter_map(ast::NameRef::cast) {
+                                    if self.found_name_ref(&name_ref, sink) {
+                                        return;
+                                    }
                                 }
                             }
                         }
@@ -544,9 +562,11 @@ impl<'a> FindUsages<'a> {
                 let finder = &Finder::new("self");
 
                 for offset in match_indices(&text, finder, search_range) {
-                    for name_ref in sema.find_nodes_at_offset_with_descend(&tree, offset) {
-                        if self.found_self_module_name_ref(&name_ref, sink) {
-                            return;
+                    if let Some(iter) = find_nodes("self", &tree, offset) {
+                        for name_ref in iter.filter_map(ast::NameRef::cast) {
+                            if self.found_self_module_name_ref(&name_ref, sink) {
+                                return;
+                            }
                         }
                     }
                 }
diff --git a/src/tools/rust-analyzer/crates/ide/src/moniker.rs b/src/tools/rust-analyzer/crates/ide/src/moniker.rs
index 852a8fd837616..07d117aff10bd 100644
--- a/src/tools/rust-analyzer/crates/ide/src/moniker.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/moniker.rs
@@ -73,8 +73,8 @@ impl MonikerResult {
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct PackageInformation {
     pub name: String,
-    pub repo: String,
-    pub version: String,
+    pub repo: Option,
+    pub version: Option,
 }
 
 pub(crate) fn crate_for_file(db: &RootDatabase, file_id: FileId) -> Option {
@@ -256,18 +256,18 @@ pub(crate) fn def_to_moniker(
             let (name, repo, version) = match krate.origin(db) {
                 CrateOrigin::CratesIo { repo, name } => (
                     name.unwrap_or(krate.display_name(db)?.canonical_name().to_string()),
-                    repo?,
-                    krate.version(db)?,
+                    repo,
+                    krate.version(db),
                 ),
                 CrateOrigin::Lang(lang) => (
                     krate.display_name(db)?.canonical_name().to_string(),
-                    "https://github.com/rust-lang/rust/".to_string(),
-                    match lang {
+                    Some("https://github.com/rust-lang/rust/".to_string()),
+                    Some(match lang {
                         LangCrateOrigin::Other => {
                             "https://github.com/rust-lang/rust/library/".into()
                         }
                         lang => format!("https://github.com/rust-lang/rust/library/{lang}",),
-                    },
+                    }),
                 ),
             };
             PackageInformation { name, repo, version }
@@ -315,7 +315,7 @@ pub mod module {
 }
 "#,
             "foo::module::func",
-            r#"PackageInformation { name: "foo", repo: "https://a.b/foo.git", version: "0.1.0" }"#,
+            r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#,
             MonikerKind::Import,
         );
         check_moniker(
@@ -331,7 +331,7 @@ pub mod module {
 }
 "#,
             "foo::module::func",
-            r#"PackageInformation { name: "foo", repo: "https://a.b/foo.git", version: "0.1.0" }"#,
+            r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#,
             MonikerKind::Export,
         );
     }
@@ -348,7 +348,7 @@ pub mod module {
 }
 "#,
             "foo::module::MyTrait::func",
-            r#"PackageInformation { name: "foo", repo: "https://a.b/foo.git", version: "0.1.0" }"#,
+            r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#,
             MonikerKind::Export,
         );
     }
@@ -365,7 +365,7 @@ pub mod module {
 }
 "#,
             "foo::module::MyTrait::MY_CONST",
-            r#"PackageInformation { name: "foo", repo: "https://a.b/foo.git", version: "0.1.0" }"#,
+            r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#,
             MonikerKind::Export,
         );
     }
@@ -382,7 +382,7 @@ pub mod module {
 }
 "#,
             "foo::module::MyTrait::MyType",
-            r#"PackageInformation { name: "foo", repo: "https://a.b/foo.git", version: "0.1.0" }"#,
+            r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#,
             MonikerKind::Export,
         );
     }
@@ -405,7 +405,7 @@ pub mod module {
 }
 "#,
             "foo::module::MyStruct::MyTrait::func",
-            r#"PackageInformation { name: "foo", repo: "https://a.b/foo.git", version: "0.1.0" }"#,
+            r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#,
             MonikerKind::Export,
         );
     }
@@ -425,7 +425,7 @@ pub struct St {
 }
 "#,
             "foo::St::a",
-            r#"PackageInformation { name: "foo", repo: "https://a.b/foo.git", version: "0.1.0" }"#,
+            r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#,
             MonikerKind::Import,
         );
     }
diff --git a/src/tools/rust-analyzer/crates/ide/src/rename.rs b/src/tools/rust-analyzer/crates/ide/src/rename.rs
index fe44856dcad2a..b4df0437050f4 100644
--- a/src/tools/rust-analyzer/crates/ide/src/rename.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/rename.rs
@@ -40,7 +40,9 @@ pub(crate) fn prepare_rename(
             if def.range_for_rename(&sema).is_none() {
                 bail!("No references found at position")
             }
-            let frange = sema.original_range(name_like.syntax());
+            let Some(frange) = sema.original_range_opt(name_like.syntax()) else {
+                bail!("No references found at position");
+            };
 
             always!(
                 frange.range.contains_inclusive(position.offset)
@@ -51,7 +53,7 @@ pub(crate) fn prepare_rename(
         .reduce(|acc, cur| match (acc, cur) {
             // ensure all ranges are the same
             (Ok(acc_inner), Ok(cur_inner)) if acc_inner == cur_inner => Ok(acc_inner),
-            (Err(e), _) => Err(e),
+            (e @ Err(_), _) | (_, e @ Err(_)) => e,
             _ => bail!("inconsistent text range"),
         });
 
@@ -2249,4 +2251,33 @@ fn foo((bar | bar | bar): ()) {
 "#,
         );
     }
+
+    #[test]
+    fn regression_13498() {
+        check(
+            "Testing",
+            r"
+mod foo {
+    pub struct Test$0;
+}
+
+use foo::Test as Tester;
+
+fn main() {
+    let t = Tester;
+}
+",
+            r"
+mod foo {
+    pub struct Testing;
+}
+
+use foo::Testing as Tester;
+
+fn main() {
+    let t = Tester;
+}
+",
+        )
+    }
 }
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 fedc1a4358272..7486b20293a66 100644
--- a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs
@@ -149,7 +149,7 @@ fn signature_help_for_call(
                 variant.name(db)
             );
         }
-        hir::CallableKind::Closure | hir::CallableKind::FnPtr => (),
+        hir::CallableKind::Closure | hir::CallableKind::FnPtr | hir::CallableKind::Other => (),
     }
 
     res.signature.push('(');
@@ -189,9 +189,10 @@ fn signature_help_for_call(
         hir::CallableKind::Function(func) if callable.return_type().contains_unknown() => {
             render(func.ret_type(db))
         }
-        hir::CallableKind::Function(_) | hir::CallableKind::Closure | hir::CallableKind::FnPtr => {
-            render(callable.return_type())
-        }
+        hir::CallableKind::Function(_)
+        | hir::CallableKind::Closure
+        | hir::CallableKind::FnPtr
+        | hir::CallableKind::Other => render(callable.return_type()),
         hir::CallableKind::TupleStruct(_) | hir::CallableKind::TupleEnumVariant(_) => {}
     }
     Some(res)
@@ -387,10 +388,9 @@ mod tests {
     }
 
     fn check(ra_fixture: &str, expect: Expect) {
-        // Implicitly add `Sized` to avoid noisy `T: ?Sized` in the results.
         let fixture = format!(
             r#"
-#[lang = "sized"] trait Sized {{}}
+//- minicore: sized, fn
 {ra_fixture}
             "#
         );
@@ -1331,4 +1331,19 @@ fn f() {
             "#]],
         );
     }
+
+    #[test]
+    fn help_for_generic_call() {
+        check(
+            r#"
+fn f i32>(f: F) {
+    f($0)
+}
+"#,
+            expect![[r#"
+                (u8, u16) -> i32
+                 ^^  ---
+            "#]],
+        );
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs
index 5ff347b9bd722..c74ddabb17770 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs
@@ -106,12 +106,12 @@ impl LsifManager<'_> {
                 manager: "cargo".to_string(),
                 uri: None,
                 content: None,
-                repository: Some(lsif::Repository {
-                    url: pi.repo,
+                repository: pi.repo.map(|url| lsif::Repository {
+                    url,
                     r#type: "git".to_string(),
                     commit_id: None,
                 }),
-                version: Some(pi.version),
+                version: pi.version,
             }));
         self.package_map.insert(package_information, result_set_id);
         result_set_id
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs
index 16298862b50f3..ca7ba896b67cf 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs
@@ -231,7 +231,7 @@ fn token_to_symbol(token: &TokenStaticData) -> Option {
         package: Some(scip_types::Package {
             manager: "cargo".to_string(),
             name: package_name,
-            version,
+            version: version.unwrap_or_else(|| ".".to_string()),
             ..Default::default()
         })
         .into(),
@@ -415,4 +415,42 @@ pub mod module {
             "",
         );
     }
+
+    #[test]
+    fn global_symbol_for_pub_struct() {
+        check_symbol(
+            r#"
+    //- /lib.rs crate:main
+    mod foo;
+
+    fn main() {
+        let _bar = foo::Bar { i: 0 };
+    }
+    //- /foo.rs
+    pub struct Bar$0 {
+        pub i: i32,
+    }
+    "#,
+            "rust-analyzer cargo main . foo/Bar#",
+        );
+    }
+
+    #[test]
+    fn global_symbol_for_pub_struct_reference() {
+        check_symbol(
+            r#"
+    //- /lib.rs crate:main
+    mod foo;
+
+    fn main() {
+        let _bar = foo::Bar$0 { i: 0 };
+    }
+    //- /foo.rs
+    pub struct Bar {
+        pub i: i32,
+    }
+    "#,
+            "rust-analyzer cargo main . foo/Bar#",
+        );
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
index 1ed8f2bb5f35a..4072ae585dbd9 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
@@ -56,6 +56,9 @@ mod patch_old_style;
 // parsing the old name.
 config_data! {
     struct ConfigData {
+        /// Whether to insert #[must_use] when generating `as_` methods
+        /// for enum variants.
+        assist_emitMustUse: bool               = "false",
         /// Placeholder expression to use for missing expressions in assists.
         assist_expressionFillDefault: ExprFillDefaultDef              = "\"todo\"",
 
@@ -1276,6 +1279,7 @@ impl Config {
             allowed: None,
             insert_use: self.insert_use_config(),
             prefer_no_std: self.data.imports_prefer_no_std,
+            assist_emit_must_use: self.data.assist_emitMustUse,
         }
     }
 
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs
index 4057a75e7c1e6..8c26009add2bb 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs
@@ -334,6 +334,10 @@ pub fn block_expr(
     ast_from_text(&format!("fn f() {buf}"))
 }
 
+pub fn tail_only_block_expr(tail_expr: ast::Expr) -> ast::BlockExpr {
+    ast_from_text(&format!("fn f() {{ {tail_expr} }}"))
+}
+
 /// Ideally this function wouldn't exist since it involves manual indenting.
 /// It differs from `make::block_expr` by also supporting comments.
 ///
@@ -656,6 +660,22 @@ pub fn let_stmt(
     };
     ast_from_text(&format!("fn f() {{ {text} }}"))
 }
+
+pub fn let_else_stmt(
+    pattern: ast::Pat,
+    ty: Option,
+    expr: ast::Expr,
+    diverging: ast::BlockExpr,
+) -> ast::LetStmt {
+    let mut text = String::new();
+    format_to!(text, "let {pattern}");
+    if let Some(ty) = ty {
+        format_to!(text, ": {ty}");
+    }
+    format_to!(text, " = {expr} else {diverging};");
+    ast_from_text(&format!("fn f() {{ {text} }}"))
+}
+
 pub fn expr_stmt(expr: ast::Expr) -> ast::ExprStmt {
     let semi = if expr.is_block_like() { "" } else { ";" };
     ast_from_text(&format!("fn f() {{ {expr}{semi} (); }}"))
diff --git a/src/tools/rust-analyzer/docs/dev/guide.md b/src/tools/rust-analyzer/docs/dev/guide.md
index 52a13da31c5d3..56a68ef043790 100644
--- a/src/tools/rust-analyzer/docs/dev/guide.md
+++ b/src/tools/rust-analyzer/docs/dev/guide.md
@@ -338,7 +338,7 @@ The algorithm for building a tree of modules is to start with a crate root
 declarations and recursively process child modules. This is handled by the
 [`module_tree_query`], with two slight variations.
 
-[`module_tree_query`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/hir/src/module_tree.rs#L116-L123
+[`module_tree_query`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/module_tree.rs#L115-L133
 
 First, rust-analyzer builds a module tree for all crates in a source root
 simultaneously. The main reason for this is historical (`module_tree` predates
@@ -361,7 +361,7 @@ the same, we don't have to re-execute [`module_tree_query`]. In fact, we only
 need to re-execute it when we add/remove new files or when we change mod
 declarations.
 
-[`submodules_query`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/hir/src/module_tree.rs#L41
+[`submodules_query`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/module_tree.rs#L41
 
 We store the resulting modules in a `Vec`-based indexed arena. The indices in
 the arena becomes module IDs. And this brings us to the next topic:
@@ -389,8 +389,8 @@ integers which can "intern" a location and return an integer ID back. The salsa
 database we use includes a couple of [interners]. How to "garbage collect"
 unused locations is an open question.
 
-[`LocationInterner`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/base_db/src/loc2id.rs#L65-L71
-[interners]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/hir/src/db.rs#L22-L23
+[`LocationInterner`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_db/src/loc2id.rs#L65-L71
+[interners]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/db.rs#L22-L23
 
 For example, we use `LocationInterner` to assign IDs to definitions of functions,
 structs, enums, etc. The location, [`DefLoc`] contains two bits of information:
@@ -404,7 +404,7 @@ using offsets, text ranges or syntax trees as keys and values for queries. What
 we do instead is we store "index" of the item among all of the items of a file
 (so, a positional based ID, but localized to a single file).
 
-[`DefLoc`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/hir/src/ids.rs#L127-L139
+[`DefLoc`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/ids.rs#L129-L139
 
 One thing we've glossed over for the time being is support for macros. We have
 only proof of concept handling of macros at the moment, but they are extremely
@@ -437,7 +437,7 @@ terms of `HirFileId`! This does not recur infinitely though: any chain of
 `HirFileId`s bottoms out in `HirFileId::FileId`, that is, some source file
 actually written by the user.
 
-[`HirFileId`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/hir/src/ids.rs#L18-L125
+[`HirFileId`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/ids.rs#L31-L93
 
 Now that we understand how to identify a definition, in a source or in a
 macro-generated file, we can discuss name resolution a bit.
@@ -451,14 +451,13 @@ each module into a position-independent representation which does not change if
 we modify bodies of the items. After that we [loop] resolving all imports until
 we've reached a fixed point.
 
-[lower]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/hir/src/nameres/lower.rs#L113-L117
-[loop]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/hir/src/nameres.rs#L186-L196
-
+[lower]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/nameres/lower.rs#L113-L147
+[loop]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/nameres.rs#L186-L196
 And, given all our preparation with IDs and a position-independent representation,
 it is satisfying to [test] that typing inside function body does not invalidate
 name resolution results.
 
-[test]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/hir/src/nameres/tests.rs#L376
+[test]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/nameres/tests.rs#L376
 
 An interesting fact about name resolution is that it "erases" all of the
 intermediate paths from the imports: in the end, we know which items are defined
@@ -493,10 +492,10 @@ there's an intermediate [projection query] which returns only the first
 position-independent part of the lowering. The result of this query is stable.
 Naturally, name resolution [uses] this stable projection query.
 
-[imports]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/hir/src/nameres/lower.rs#L52-L59
-[`SourceMap`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/hir/src/nameres/lower.rs#L52-L59
-[projection query]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/hir/src/nameres/lower.rs#L97-L103
-[uses]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/hir/src/query_definitions.rs#L49
+[imports]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/nameres/lower.rs#L52-L59
+[`SourceMap`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/nameres/lower.rs#L52-L59
+[projection query]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/nameres/lower.rs#L97-L103
+[uses]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/query_definitions.rs#L49
 
 ## Type inference
 
@@ -518,10 +517,10 @@ construct a mapping from `ExprId`s to types.
 
 [@flodiebold]: https://github.com/flodiebold
 [#327]: https://github.com/rust-lang/rust-analyzer/pull/327
-[lower the AST]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/hir/src/expr.rs
-[positional ID]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/hir/src/expr.rs#L13-L15
-[a source map]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/hir/src/expr.rs#L41-L44
-[type inference]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/hir/src/ty.rs#L1208-L1223
+[lower the AST]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/expr.rs
+[positional ID]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/expr.rs#L13-L15
+[a source map]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/expr.rs#L41-L44
+[type inference]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/ty.rs#L1208-L1223
 
 ## Tying it all together: completion
 
@@ -563,10 +562,11 @@ the type to completion.
 [catch]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_lsp_server/src/main_loop.rs#L436-L442
 [the handler]: https://salsa.zulipchat.com/#narrow/stream/181542-rfcs.2Fsalsa-query-group/topic/design.20next.20steps
 [ask analysis for completion]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ide_api/src/lib.rs#L439-L444
-[completion implementation]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ide_api/src/completion.rs#L46-L62
-[`CompletionContext`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ide_api/src/completion/completion_context.rs#L14-L37
-["IntelliJ Trick"]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ide_api/src/completion/completion_context.rs#L72-L75
-[find an ancestor `fn` node]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ide_api/src/completion/completion_context.rs#L116-L120
-[semantic model]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ide_api/src/completion/completion_context.rs#L123
-[series of independent completion routines]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ide_api/src/completion.rs#L52-L59
-[`complete_dot`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ide_api/src/completion/complete_dot.rs#L6-L22
+[ask analysis for completion]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_ide_api/src/lib.rs#L439-L444
+[completion implementation]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_ide_api/src/completion.rs#L46-L62
+[`CompletionContext`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_ide_api/src/completion/completion_context.rs#L14-L37
+["IntelliJ Trick"]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_ide_api/src/completion/completion_context.rs#L72-L75
+[find an ancestor `fn` node]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_ide_api/src/completion/completion_context.rs#L116-L120
+[semantic model]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_ide_api/src/completion/completion_context.rs#L123
+[series of independent completion routines]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_ide_api/src/completion.rs#L52-L59
+[`complete_dot`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_ide_api/src/completion/complete_dot.rs#L6-L22
diff --git a/src/tools/rust-analyzer/docs/user/generated_config.adoc b/src/tools/rust-analyzer/docs/user/generated_config.adoc
index 502833de72c16..36794efe42726 100644
--- a/src/tools/rust-analyzer/docs/user/generated_config.adoc
+++ b/src/tools/rust-analyzer/docs/user/generated_config.adoc
@@ -1,3 +1,9 @@
+[[rust-analyzer.assist.emitMustUse]]rust-analyzer.assist.emitMustUse (default: `false`)::
++
+--
+Whether to insert #[must_use] when generating `as_` methods
+for enum variants.
+--
 [[rust-analyzer.assist.expressionFillDefault]]rust-analyzer.assist.expressionFillDefault (default: `"todo"`)::
 +
 --
diff --git a/src/tools/rust-analyzer/docs/user/manual.adoc b/src/tools/rust-analyzer/docs/user/manual.adoc
index c30838e5f5e1e..49500e390a502 100644
--- a/src/tools/rust-analyzer/docs/user/manual.adoc
+++ b/src/tools/rust-analyzer/docs/user/manual.adoc
@@ -487,6 +487,12 @@ https://docs.helix-editor.com/[Helix] supports LSP by default.
 However, it won't install `rust-analyzer` automatically.
 You can follow instructions for installing <>.
 
+=== Crates
+
+There is a package named `ra_ap_rust_analyzer` available on https://crates.io/crates/ra_ap_rust-analyzer[crates.io], for someone who wants to use it programmatically.
+
+For more details, see https://github.com/rust-lang/rust-analyzer/blob/master/.github/workflows/publish.yml[the publish workflow].
+
 == Troubleshooting
 
 Start with looking at the rust-analyzer version.
diff --git a/src/tools/rust-analyzer/editors/code/package-lock.json b/src/tools/rust-analyzer/editors/code/package-lock.json
index a72865d4fe44e..0b25564e28d37 100644
--- a/src/tools/rust-analyzer/editors/code/package-lock.json
+++ b/src/tools/rust-analyzer/editors/code/package-lock.json
@@ -23,7 +23,7 @@
                 "esbuild": "^0.14.48",
                 "eslint": "^8.19.0",
                 "eslint-config-prettier": "^8.5.0",
-                "ovsx": "^0.5.1",
+                "ovsx": "^0.5.2",
                 "prettier": "^2.7.1",
                 "tslib": "^2.4.0",
                 "typescript": "^4.7.4",
@@ -2874,9 +2874,9 @@
             }
         },
         "node_modules/ovsx": {
-            "version": "0.5.1",
-            "resolved": "https://registry.npmjs.org/ovsx/-/ovsx-0.5.1.tgz",
-            "integrity": "sha512-3OWq0l7DuVHi2bd2aQe5+QVQlFIqvrcw3/2vGXL404L6Tr+R4QHtzfnYYghv8CCa85xJHjU0RhcaC7pyXkAUbg==",
+            "version": "0.5.2",
+            "resolved": "https://registry.npmjs.org/ovsx/-/ovsx-0.5.2.tgz",
+            "integrity": "sha512-UbLultRCk46WddeA0Cly4hoRhzBJUiLgbIEViXlgOvV54LbsppClDkMLoCevUUBHoiNdMX2NuiSgURAEXgCZdw==",
             "dev": true,
             "dependencies": {
                 "commander": "^6.1.0",
@@ -5958,9 +5958,9 @@
             }
         },
         "ovsx": {
-            "version": "0.5.1",
-            "resolved": "https://registry.npmjs.org/ovsx/-/ovsx-0.5.1.tgz",
-            "integrity": "sha512-3OWq0l7DuVHi2bd2aQe5+QVQlFIqvrcw3/2vGXL404L6Tr+R4QHtzfnYYghv8CCa85xJHjU0RhcaC7pyXkAUbg==",
+            "version": "0.5.2",
+            "resolved": "https://registry.npmjs.org/ovsx/-/ovsx-0.5.2.tgz",
+            "integrity": "sha512-UbLultRCk46WddeA0Cly4hoRhzBJUiLgbIEViXlgOvV54LbsppClDkMLoCevUUBHoiNdMX2NuiSgURAEXgCZdw==",
             "dev": true,
             "requires": {
                 "commander": "^6.1.0",
diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json
index 6771cad28a792..1a97a9c089375 100644
--- a/src/tools/rust-analyzer/editors/code/package.json
+++ b/src/tools/rust-analyzer/editors/code/package.json
@@ -49,7 +49,7 @@
         "esbuild": "^0.14.48",
         "eslint": "^8.19.0",
         "eslint-config-prettier": "^8.5.0",
-        "ovsx": "^0.5.1",
+        "ovsx": "^0.5.2",
         "prettier": "^2.7.1",
         "tslib": "^2.4.0",
         "typescript": "^4.7.4",
@@ -100,22 +100,32 @@
             {
                 "command": "rust-analyzer.syntaxTree",
                 "title": "Show Syntax Tree",
-                "category": "rust-analyzer"
+                "category": "rust-analyzer (debug command)"
             },
             {
                 "command": "rust-analyzer.viewHir",
                 "title": "View Hir",
-                "category": "rust-analyzer"
+                "category": "rust-analyzer (debug command)"
             },
             {
                 "command": "rust-analyzer.viewFileText",
                 "title": "View File Text (as seen by the server)",
-                "category": "rust-analyzer"
+                "category": "rust-analyzer (debug command)"
             },
             {
                 "command": "rust-analyzer.viewItemTree",
                 "title": "Debug ItemTree",
-                "category": "rust-analyzer"
+                "category": "rust-analyzer (debug command)"
+            },
+            {
+                "command": "rust-analyzer.shuffleCrateGraph",
+                "title": "Shuffle Crate Graph",
+                "category": "rust-analyzer (debug command)"
+            },
+            {
+                "command": "rust-analyzer.memoryUsage",
+                "title": "Memory Usage (Clears Database)",
+                "category": "rust-analyzer (debug command)"
             },
             {
                 "command": "rust-analyzer.viewCrateGraph",
@@ -172,16 +182,6 @@
                 "title": "Status",
                 "category": "rust-analyzer"
             },
-            {
-                "command": "rust-analyzer.memoryUsage",
-                "title": "Memory Usage (Clears Database)",
-                "category": "rust-analyzer"
-            },
-            {
-                "command": "rust-analyzer.shuffleCrateGraph",
-                "title": "Shuffle Crate Graph",
-                "category": "rust-analyzer"
-            },
             {
                 "command": "rust-analyzer.reloadWorkspace",
                 "title": "Reload workspace",
@@ -397,6 +397,11 @@
                     "type": "boolean"
                 },
                 "$generated-start": {},
+                "rust-analyzer.assist.emitMustUse": {
+                    "markdownDescription": "Whether to insert #[must_use] when generating `as_` methods\nfor enum variants.",
+                    "default": false,
+                    "type": "boolean"
+                },
                 "rust-analyzer.assist.expressionFillDefault": {
                     "markdownDescription": "Placeholder expression to use for missing expressions in assists.",
                     "default": "todo",
diff --git a/src/tools/rust-analyzer/triagebot.toml b/src/tools/rust-analyzer/triagebot.toml
index fa0824ac53c0a..a910e012b7349 100644
--- a/src/tools/rust-analyzer/triagebot.toml
+++ b/src/tools/rust-analyzer/triagebot.toml
@@ -1 +1,11 @@
 [assign]
+
+[shortcut]
+
+[relabel]
+allow-unauthenticated = [
+    "S-*",
+]
+
+[autolabel."S-waiting-on-review"]
+new_pr = true
diff --git a/src/tools/rustdoc-gui/tester.js b/src/tools/rustdoc-gui/tester.js
index 70d5f94472f6b..d40d9a3cb542a 100644
--- a/src/tools/rustdoc-gui/tester.js
+++ b/src/tools/rustdoc-gui/tester.js
@@ -149,6 +149,7 @@ async function main(argv) {
         // This is more convenient that setting fields one by one.
         let args = [
             "--variable", "DOC_PATH", opts["doc_folder"], "--enable-fail-on-js-error",
+            "--allow-file-access-from-files",
         ];
         if (opts["debug"]) {
             debug = true;