Repository: rust-lang/rust-analyzer
Pull request #13805 (2022-12-20 16:40:43)

Complete enum variants without parens when snippets are disabled
This handles the portion of #13767 that bothered me, but I can try to work on the other parts we discussed if needed.


Patch:
diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs
index 7cf4fb1050..a42f8a0ac0 100644
--- a/crates/hir-ty/src/infer.rs
+++ b/crates/hir-ty/src/infer.rs
@@ -535,9 +535,6 @@ impl<'a> InferenceContext<'a> {
         for (_, subst) in result.method_resolutions.values_mut() {
             *subst = table.resolve_completely(subst.clone());
         }
-        for (_, subst) in result.assoc_resolutions.values_mut() {
-            *subst = table.resolve_completely(subst.clone());
-        }
         for adjustment in result.expr_adjustments.values_mut().flatten() {
             adjustment.target = table.resolve_completely(adjustment.target.clone());
         }
diff --git a/crates/ide-completion/src/completions/record.rs b/crates/ide-completion/src/completions/record.rs
index 5d96fbd30a..6743ec897f 100644
--- a/crates/ide-completion/src/completions/record.rs
+++ b/crates/ide-completion/src/completions/record.rs
@@ -124,7 +124,12 @@ fn complete_fields(
 
 #[cfg(test)]
 mod tests {
-    use crate::tests::check_edit;
+    use ide_db::SnippetCap;
+
+    use crate::{
+        tests::{check_edit, check_edit_with_config, TEST_CONFIG},
+        CompletionConfig,
+    };
 
     #[test]
     fn literal_struct_completion_edit() {
@@ -151,6 +156,37 @@ fn baz() {
         )
     }
 
+    #[test]
+    fn enum_variant_no_snippets() {
+        let conf = CompletionConfig { snippet_cap: SnippetCap::new(false), ..TEST_CONFIG };
+        check_edit_with_config(
+            conf,
+            "Variant()",
+            r#"
+enum Enum {
+    Variant(usize),
+}
+
+impl Enum {
+    fn new(u: usize) -> Self {
+        Self::Va$0
+    }
+}
+"#,
+            r#"
+enum Enum {
+    Variant(usize),
+}
+
+impl Enum {
+    fn new(u: usize) -> Self {
+        Self::Variant
+    }
+}
+"#,
+        )
+    }
+
     #[test]
     fn literal_struct_impl_self_completion() {
         check_edit(
diff --git a/crates/ide-completion/src/render/literal.rs b/crates/ide-completion/src/render/literal.rs
index 0c791ac570..3aeb69258e 100644
--- a/crates/ide-completion/src/render/literal.rs
+++ b/crates/ide-completion/src/render/literal.rs
@@ -96,7 +96,7 @@ fn render(
     if !should_add_parens {
         kind = StructKind::Unit;
     }
-    let label = format_literal_label(&qualified_name, kind);
+    let label = format_literal_label(&qualified_name, kind, snippet_cap);
     let lookup = if qualified {
         format_literal_lookup(&short_qualified_name.to_string(), kind)
     } else {
diff --git a/crates/ide-completion/src/render/pattern.rs b/crates/ide-completion/src/render/pattern.rs
index c845ff21aa..21b4bc2174 100644
--- a/crates/ide-completion/src/render/pattern.rs
+++ b/crates/ide-completion/src/render/pattern.rs
@@ -33,7 +33,7 @@ pub(crate) fn render_struct_pat(
     let name = local_name.unwrap_or_else(|| strukt.name(ctx.db()));
     let (name, escaped_name) = (name.unescaped().to_smol_str(), name.to_smol_str());
     let kind = strukt.kind(ctx.db());
-    let label = format_literal_label(name.as_str(), kind);
+    let label = format_literal_label(name.as_str(), kind, ctx.snippet_cap());
     let lookup = format_literal_lookup(name.as_str(), kind);
     let pat = render_pat(&ctx, pattern_ctx, &escaped_name, kind, &visible_fields, fields_omitted)?;
 
@@ -67,7 +67,7 @@ pub(crate) fn render_variant_pat(
         }
         _ => {
             let kind = variant.kind(ctx.db());
-            let label = format_literal_label(name.as_str(), kind);
+            let label = format_literal_label(name.as_str(), kind, ctx.snippet_cap());
             let lookup = format_literal_lookup(name.as_str(), kind);
             let pat = render_pat(
                 &ctx,
diff --git a/crates/ide-completion/src/render/union_literal.rs b/crates/ide-completion/src/render/union_literal.rs
index 54e97dd57b..2d55a1bade 100644
--- a/crates/ide-completion/src/render/union_literal.rs
+++ b/crates/ide-completion/src/render/union_literal.rs
@@ -24,7 +24,7 @@ pub(crate) fn render_union_literal(
         Some(p) => (p.unescaped().to_string(), p.to_string()),
         None => (name.unescaped().to_string(), name.to_string()),
     };
-    let label = format_literal_label(&name.to_smol_str(), StructKind::Record);
+    let label = format_literal_label(&name.to_smol_str(), StructKind::Record, ctx.snippet_cap());
     let lookup = format_literal_lookup(&name.to_smol_str(), StructKind::Record);
     let mut item = CompletionItem::new(
         CompletionItemKind::SymbolKind(SymbolKind::Union),
diff --git a/crates/ide-completion/src/render/variant.rs b/crates/ide-completion/src/render/variant.rs
index 24e6abdc9a..d69906a706 100644
--- a/crates/ide-completion/src/render/variant.rs
+++ b/crates/ide-completion/src/render/variant.rs
@@ -48,6 +48,9 @@ pub(crate) fn render_tuple_lit(
     fields: &[hir::Field],
     path: &str,
 ) -> RenderedLiteral {
+    if snippet_cap.is_none() {
+        return RenderedLiteral { literal: format!("{}", path), detail: format!("{}", path) };
+    }
     let completions = fields.iter().enumerate().format_with(", ", |(idx, _), f| {
         if snippet_cap.is_some() {
             f(&format_args!("${{{}:()}}", idx + 1))
@@ -87,7 +90,14 @@ pub(crate) fn visible_fields(
 }
 
 /// Format a struct, etc. literal option for display in the completions menu.
-pub(crate) fn format_literal_label(name: &str, kind: StructKind) -> SmolStr {
+pub(crate) fn format_literal_label(
+    name: &str,
+    kind: StructKind,
+    snippet_cap: Option<SnippetCap>,
+) -> SmolStr {
+    if snippet_cap.is_none() {
+        return name.into();
+    }
     match kind {
         StructKind::Tuple => SmolStr::from_iter([name, "(…)"]),
         StructKind::Record => SmolStr::from_iter([name, " {…}"]),
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs
index 152f31b3a5..08ce7be181 100644
--- a/crates/ide/src/inlay_hints.rs
+++ b/crates/ide/src/inlay_hints.rs
@@ -1,26 +1,24 @@
 use std::fmt;
 
 use either::Either;
-use hir::{known, HasVisibility, HirDisplay, Semantics};
-use ide_db::{base_db::FileRange, famous_defs::FamousDefs, RootDatabase};
+use hir::{
+    known, Adjust, AutoBorrow, Callable, HasVisibility, HirDisplay, Mutability, OverloadedDeref,
+    PointerCast, Safety, Semantics, TypeInfo,
+};
+use ide_db::{
+    base_db::FileRange, famous_defs::FamousDefs, syntax_helpers::node_ext::walk_ty, FxHashMap,
+    RootDatabase,
+};
 use itertools::Itertools;
+use stdx::to_lower_snake_case;
 use syntax::{
-    ast::{self, AstNode},
-    match_ast, NodeOrToken, SyntaxNode, TextRange, TextSize,
+    ast::{self, AstNode, HasArgList, HasGenericParams, HasName, UnaryOp},
+    match_ast, Direction, NodeOrToken, SmolStr, SyntaxKind, SyntaxNode, SyntaxToken, TextRange,
+    TextSize, T,
 };
 
 use crate::FileId;
 
-mod closing_brace;
-mod implicit_static;
-mod fn_lifetime_fn;
-mod closure_ret;
-mod adjustment;
-mod chaining;
-mod param_name;
-mod binding_mode;
-mod bind_pat;
-
 #[derive(Clone, Debug, PartialEq, Eq)]
 pub struct InlayHintsConfig {
     pub render_colons: bool,
@@ -67,11 +65,10 @@ pub enum InlayKind {
     ClosureReturnTypeHint,
     GenericParamListHint,
     AdjustmentHint,
+    AdjustmentHintClosingParenthesis,
     LifetimeHint,
     ParameterHint,
     TypeHint,
-    OpeningParenthesis,
-    ClosingParenthesis,
 }
 
 #[derive(Debug)]
@@ -229,18 +226,18 @@ fn hints(
     file_id: FileId,
     node: SyntaxNode,
 ) {
-    closing_brace::hints(hints, sema, config, file_id, node.clone());
+    closing_brace_hints(hints, sema, config, file_id, node.clone());
     match_ast! {
         match node {
             ast::Expr(expr) => {
-                chaining::hints(hints, sema, &famous_defs, config, file_id, &expr);
-                adjustment::hints(hints, sema, config, &expr);
+                chaining_hints(hints, sema, &famous_defs, config, file_id, &expr);
+                adjustment_hints(hints, sema, config, &expr);
                 match expr {
-                    ast::Expr::CallExpr(it) => param_name::hints(hints, sema, config, ast::Expr::from(it)),
+                    ast::Expr::CallExpr(it) => param_name_hints(hints, sema, config, ast::Expr::from(it)),
                     ast::Expr::MethodCallExpr(it) => {
-                        param_name::hints(hints, sema, config, ast::Expr::from(it))
+                        param_name_hints(hints, sema, config, ast::Expr::from(it))
                     }
-                    ast::Expr::ClosureExpr(it) => closure_ret::hints(hints, sema, &famous_defs, config, file_id, it),
+                    ast::Expr::ClosureExpr(it) => closure_ret_hints(hints, sema, &famous_defs, config, file_id, it),
                     // We could show reborrows for all expressions, but usually that is just noise to the user
                     // and the main point here is to show why "moving" a mutable reference doesn't necessarily move it
                     // ast::Expr::PathExpr(_) => reborrow_hints(hints, sema, config, &expr),
@@ -248,19 +245,19 @@ fn hints(
                 }
             },
             ast::Pat(it) => {
-                binding_mode::hints(hints, sema, config, &it);
+                binding_mode_hints(hints, sema, config, &it);
                 if let ast::Pat::IdentPat(it) = it {
-                    bind_pat::hints(hints, sema, config, file_id, &it);
+                    bind_pat_hints(hints, sema, config, file_id, &it);
                 }
                 Some(())
             },
             ast::Item(it) => match it {
                 // FIXME: record impl lifetimes so they aren't being reused in assoc item lifetime inlay hints
                 ast::Item::Impl(_) => None,
-                ast::Item::Fn(it) => fn_lifetime_fn::hints(hints, config, it),
+                ast::Item::Fn(it) => fn_lifetime_fn_hints(hints, config, it),
                 // static type elisions
-                ast::Item::Static(it) => implicit_static::hints(hints, config, Either::Left(it)),
-                ast::Item::Const(it) => implicit_static::hints(hints, config, Either::Right(it)),
+                ast::Item::Static(it) => implicit_static_hints(hints, config, Either::Left(it)),
+                ast::Item::Const(it) => implicit_static_hints(hints, config, Either::Right(it)),
                 _ => None,
             },
             // FIXME: fn-ptr type, dyn fn type, and trait object type elisions
@@ -270,6 +267,730 @@ fn hints(
     };
 }
 
+fn closing_brace_hints(
+    acc: &mut Vec<InlayHint>,
+    sema: &Semantics<'_, RootDatabase>,
+    config: &InlayHintsConfig,
+    file_id: FileId,
+    node: SyntaxNode,
+) -> Option<()> {
+    let min_lines = config.closing_brace_hints_min_lines?;
+
+    let name = |it: ast::Name| it.syntax().text_range();
+
+    let mut closing_token;
+    let (label, name_range) = if let Some(item_list) = ast::AssocItemList::cast(node.clone()) {
+        closing_token = item_list.r_curly_token()?;
+
+        let parent = item_list.syntax().parent()?;
+        match_ast! {
+            match parent {
+                ast::Impl(imp) => {
+                    let imp = sema.to_def(&imp)?;
+                    let ty = imp.self_ty(sema.db);
+                    let trait_ = imp.trait_(sema.db);
+                    let hint_text = match trait_ {
+                        Some(tr) => format!("impl {} for {}", tr.name(sema.db), ty.display_truncated(sema.db, config.max_length)),
+                        None => format!("impl {}", ty.display_truncated(sema.db, config.max_length)),
+                    };
+                    (hint_text, None)
+                },
+                ast::Trait(tr) => {
+                    (format!("trait {}", tr.name()?), tr.name().map(name))
+                },
+                _ => return None,
+            }
+        }
+    } else if let Some(list) = ast::ItemList::cast(node.clone()) {
+        closing_token = list.r_curly_token()?;
+
+        let module = ast::Module::cast(list.syntax().parent()?)?;
+        (format!("mod {}", module.name()?), module.name().map(name))
+    } else if let Some(block) = ast::BlockExpr::cast(node.clone()) {
+        closing_token = block.stmt_list()?.r_curly_token()?;
+
+        let parent = block.syntax().parent()?;
+        match_ast! {
+            match parent {
+                ast::Fn(it) => {
+                    // FIXME: this could include parameters, but `HirDisplay` prints too much info
+                    // and doesn't respect the max length either, so the hints end up way too long
+                    (format!("fn {}", it.name()?), it.name().map(name))
+                },
+                ast::Static(it) => (format!("static {}", it.name()?), it.name().map(name)),
+                ast::Const(it) => {
+                    if it.underscore_token().is_some() {
+                        ("const _".into(), None)
+                    } else {
+                        (format!("const {}", it.name()?), it.name().map(name))
+                    }
+                },
+                _ => return None,
+            }
+        }
+    } else if let Some(mac) = ast::MacroCall::cast(node.clone()) {
+        let last_token = mac.syntax().last_token()?;
+        if last_token.kind() != T![;] && last_token.kind() != SyntaxKind::R_CURLY {
+            return None;
+        }
+        closing_token = last_token;
+
+        (
+            format!("{}!", mac.path()?),
+            mac.path().and_then(|it| it.segment()).map(|it| it.syntax().text_range()),
+        )
+    } else {
+        return None;
+    };
+
+    if let Some(mut next) = closing_token.next_token() {
+        if next.kind() == T![;] {
+            if let Some(tok) = next.next_token() {
+                closing_token = next;
+                next = tok;
+            }
+        }
+        if !(next.kind() == SyntaxKind::WHITESPACE && next.text().contains('\n')) {
+            // Only display the hint if the `}` is the last token on the line
+            return None;
+        }
+    }
+
+    let mut lines = 1;
+    node.text().for_each_chunk(|s| lines += s.matches('\n').count());
+    if lines < min_lines {
+        return None;
+    }
+
+    let linked_location = name_range.map(|range| FileRange { file_id, range });
+    acc.push(InlayHint {
+        range: closing_token.text_range(),
+        kind: InlayKind::ClosingBraceHint,
+        label: InlayHintLabel { parts: vec![InlayHintLabelPart { text: label, linked_location }] },
+        tooltip: None, // provided by label part location
+    });
+
+    None
+}
+
+fn implicit_static_hints(
+    acc: &mut Vec<InlayHint>,
+    config: &InlayHintsConfig,
+    statik_or_const: Either<ast::Static, ast::Const>,
+) -> Option<()> {
+    if config.lifetime_elision_hints != LifetimeElisionHints::Always {
+        return None;
+    }
+
+    if let Either::Right(it) = &statik_or_const {
+        if ast::AssocItemList::can_cast(
+            it.syntax().parent().map_or(SyntaxKind::EOF, |it| it.kind()),
+        ) {
+            return None;
+        }
+    }
+
+    if let Some(ast::Type::RefType(ty)) = statik_or_const.either(|it| it.ty(), |it| it.ty()) {
+        if ty.lifetime().is_none() {
+            let t = ty.amp_token()?;
+            acc.push(InlayHint {
+                range: t.text_range(),
+                kind: InlayKind::LifetimeHint,
+                label: "'static".to_owned().into(),
+                tooltip: Some(InlayTooltip::String("Elided static lifetime".into())),
+            });
+        }
+    }
+
+    Some(())
+}
+
+fn fn_lifetime_fn_hints(
+    acc: &mut Vec<InlayHint>,
+    config: &InlayHintsConfig,
+    func: ast::Fn,
+) -> Option<()> {
+    if config.lifetime_elision_hints == LifetimeElisionHints::Never {
+        return None;
+    }
+
+    let mk_lt_hint = |t: SyntaxToken, label: String| InlayHint {
+        range: t.text_range(),
+        kind: InlayKind::LifetimeHint,
+        label: label.into(),
+        tooltip: Some(InlayTooltip::String("Elided lifetime".into())),
+    };
+
+    let param_list = func.param_list()?;
+    let generic_param_list = func.generic_param_list();
+    let ret_type = func.ret_type();
+    let self_param = param_list.self_param().filter(|it| it.amp_token().is_some());
+
+    let is_elided = |lt: &Option<ast::Lifetime>| match lt {
+        Some(lt) => matches!(lt.text().as_str(), "'_"),
+        None => true,
+    };
+
+    let potential_lt_refs = {
+        let mut acc: Vec<_> = vec![];
+        if let Some(self_param) = &self_param {
+            let lifetime = self_param.lifetime();
+            let is_elided = is_elided(&lifetime);
+            acc.push((None, self_param.amp_token(), lifetime, is_elided));
+        }
+        param_list.params().filter_map(|it| Some((it.pat(), it.ty()?))).for_each(|(pat, ty)| {
+            // FIXME: check path types
+            walk_ty(&ty, &mut |ty| match ty {
+                ast::Type::RefType(r) => {
+                    let lifetime = r.lifetime();
+                    let is_elided = is_elided(&lifetime);
+                    acc.push((
+                        pat.as_ref().and_then(|it| match it {
+                            ast::Pat::IdentPat(p) => p.name(),
+                            _ => None,
+                        }),
+                        r.amp_token(),
+                        lifetime,
+                        is_elided,
+                    ))
+                }
+                _ => (),
+            })
+        });
+        acc
+    };
+
+    // allocate names
+    let mut gen_idx_name = {
+        let mut gen = (0u8..).map(|idx| match idx {
+            idx if idx < 10 => SmolStr::from_iter(['\'', (idx + 48) as char]),
+            idx => format!("'{idx}").into(),
+        });
+        move || gen.next().unwrap_or_default()
+    };
+    let mut allocated_lifetimes = vec![];
+
+    let mut used_names: FxHashMap<SmolStr, usize> =
+        match config.param_names_for_lifetime_elision_hints {
+            true => generic_param_list
+                .iter()
+                .flat_map(|gpl| gpl.lifetime_params())
+                .filter_map(|param| param.lifetime())
+                .filter_map(|lt| Some((SmolStr::from(lt.text().as_str().get(1..)?), 0)))
+                .collect(),
+            false => Default::default(),
+        };
+    {
+        let mut potential_lt_refs = potential_lt_refs.iter().filter(|&&(.., is_elided)| is_elided);
+        if let Some(_) = &self_param {
+            if let Some(_) = potential_lt_refs.next() {
+                allocated_lifetimes.push(if config.param_names_for_lifetime_elision_hints {
+                    // self can't be used as a lifetime, so no need to check for collisions
+                    "'self".into()
+                } else {
+                    gen_idx_name()
+                });
+            }
+        }
+        potential_lt_refs.for_each(|(name, ..)| {
+            let name = match name {
+                Some(it) if config.param_names_for_lifetime_elision_hints => {
+                    if let Some(c) = used_names.get_mut(it.text().as_str()) {
+                        *c += 1;
+                        SmolStr::from(format!("'{text}{c}", text = it.text().as_str()))
+                    } else {
+                        used_names.insert(it.text().as_str().into(), 0);
+                        SmolStr::from_iter(["\'", it.text().as_str()])
+                    }
+                }
+                _ => gen_idx_name(),
+            };
+            allocated_lifetimes.push(name);
+        });
+    }
+
+    // fetch output lifetime if elision rule applies
+    let output = match potential_lt_refs.as_slice() {
+        [(_, _, lifetime, _), ..] if self_param.is_some() || potential_lt_refs.len() == 1 => {
+            match lifetime {
+                Some(lt) => match lt.text().as_str() {
+                    "'_" => allocated_lifetimes.get(0).cloned(),
+                    "'static" => None,
+                    name => Some(name.into()),
+                },
+                None => allocated_lifetimes.get(0).cloned(),
+            }
+        }
+        [..] => None,
+    };
+
+    if allocated_lifetimes.is_empty() && output.is_none() {
+        return None;
+    }
+
+    // apply hints
+    // apply output if required
+    let mut is_trivial = true;
+    if let (Some(output_lt), Some(r)) = (&output, ret_type) {
+        if let Some(ty) = r.ty() {
+            walk_ty(&ty, &mut |ty| match ty {
+                ast::Type::RefType(ty) if ty.lifetime().is_none() => {
+                    if let Some(amp) = ty.amp_token() {
+                        is_trivial = false;
+                        acc.push(mk_lt_hint(amp, output_lt.to_string()));
+                    }
+                }
+                _ => (),
+            })
+        }
+    }
+
+    if config.lifetime_elision_hints == LifetimeElisionHints::SkipTrivial && is_trivial {
+        return None;
+    }
+
+    let mut a = allocated_lifetimes.iter();
+    for (_, amp_token, _, is_elided) in potential_lt_refs {
+        if is_elided {
+            let t = amp_token?;
+            let lt = a.next()?;
+            acc.push(mk_lt_hint(t, lt.to_string()));
+        }
+    }
+
+    // generate generic param list things
+    match (generic_param_list, allocated_lifetimes.as_slice()) {
+        (_, []) => (),
+        (Some(gpl), allocated_lifetimes) => {
+            let angle_tok = gpl.l_angle_token()?;
+            let is_empty = gpl.generic_params().next().is_none();
+            acc.push(InlayHint {
+                range: angle_tok.text_range(),
+                kind: InlayKind::LifetimeHint,
+                label: format!(
+                    "{}{}",
+                    allocated_lifetimes.iter().format(", "),
+                    if is_empty { "" } else { ", " }
+                )
+                .into(),
+                tooltip: Some(InlayTooltip::String("Elided lifetimes".into())),
+            });
+        }
+        (None, allocated_lifetimes) => acc.push(InlayHint {
+            range: func.name()?.syntax().text_range(),
+            kind: InlayKind::GenericParamListHint,
+            label: format!("<{}>", allocated_lifetimes.iter().format(", "),).into(),
+            tooltip: Some(InlayTooltip::String("Elided lifetimes".into())),
+        }),
+    }
+    Some(())
+}
+
+fn closure_ret_hints(
+    acc: &mut Vec<InlayHint>,
+    sema: &Semantics<'_, RootDatabase>,
+    famous_defs: &FamousDefs<'_, '_>,
+    config: &InlayHintsConfig,
+    file_id: FileId,
+    closure: ast::ClosureExpr,
+) -> Option<()> {
+    if config.closure_return_type_hints == ClosureReturnTypeHints::Never {
+        return None;
+    }
+
+    if closure.ret_type().is_some() {
+        return None;
+    }
+
+    if !closure_has_block_body(&closure)
+        && config.closure_return_type_hints == ClosureReturnTypeHints::WithBlock
+    {
+        return None;
+    }
+
+    let param_list = closure.param_list()?;
+
+    let closure = sema.descend_node_into_attributes(closure.clone()).pop()?;
+    let ty = sema.type_of_expr(&ast::Expr::ClosureExpr(closure))?.adjusted();
+    let callable = ty.as_callable(sema.db)?;
+    let ty = callable.return_type();
+    if ty.is_unit() {
+        return None;
+    }
+    acc.push(InlayHint {
+        range: param_list.syntax().text_range(),
+        kind: InlayKind::ClosureReturnTypeHint,
+        label: hint_iterator(sema, &famous_defs, config, &ty)
+            .unwrap_or_else(|| ty.display_truncated(sema.db, config.max_length).to_string())
+            .into(),
+        tooltip: Some(InlayTooltip::HoverRanged(file_id, param_list.syntax().text_range())),
+    });
+    Some(())
+}
+
+fn adjustment_hints(
+    acc: &mut Vec<InlayHint>,
+    sema: &Semantics<'_, RootDatabase>,
+    config: &InlayHintsConfig,
+    expr: &ast::Expr,
+) -> Option<()> {
+    if config.adjustment_hints == AdjustmentHints::Never {
+        return None;
+    }
+
+    // These inherit from the inner expression which would result in duplicate hints
+    if let ast::Expr::ParenExpr(_)
+    | ast::Expr::IfExpr(_)
+    | ast::Expr::BlockExpr(_)
+    | ast::Expr::MatchExpr(_) = expr
+    {
+        return None;
+    }
+
+    let parent = expr.syntax().parent().and_then(ast::Expr::cast);
+    let descended = sema.descend_node_into_attributes(expr.clone()).pop();
+    let desc_expr = descended.as_ref().unwrap_or(expr);
+    let adjustments = sema.expr_adjustments(desc_expr).filter(|it| !it.is_empty())?;
+    let needs_parens = match parent {
+        Some(parent) => {
+            match parent {
+                ast::Expr::AwaitExpr(_)
+                | ast::Expr::CallExpr(_)
+                | ast::Expr::CastExpr(_)
+                | ast::Expr::FieldExpr(_)
+                | ast::Expr::MethodCallExpr(_)
+                | ast::Expr::TryExpr(_) => true,
+                // FIXME: shorthands need special casing, though not sure if adjustments are even valid there
+                ast::Expr::RecordExpr(_) => false,
+                ast::Expr::IndexExpr(index) => index.base().as_ref() == Some(expr),
+                _ => false,
+            }
+        }
+        None => false,
+    };
+    if needs_parens {
+        acc.push(InlayHint {
+            range: expr.syntax().text_range(),
+            kind: InlayKind::AdjustmentHint,
+            label: "(".into(),
+            tooltip: None,
+        });
+    }
+    for adjustment in adjustments.into_iter().rev() {
+        // FIXME: Add some nicer tooltips to each of these
+        let text = match adjustment {
+            Adjust::NeverToAny if config.adjustment_hints == AdjustmentHints::Always => {
+                "<never-to-any>"
+            }
+            Adjust::Deref(None) => "*",
+            Adjust::Deref(Some(OverloadedDeref(Mutability::Mut))) => "*",
+            Adjust::Deref(Some(OverloadedDeref(Mutability::Shared))) => "*",
+            Adjust::Borrow(AutoBorrow::Ref(Mutability::Shared)) => "&",
+            Adjust::Borrow(AutoBorrow::Ref(Mutability::Mut)) => "&mut ",
+            Adjust::Borrow(AutoBorrow::RawPtr(Mutability::Shared)) => "&raw const ",
+            Adjust::Borrow(AutoBorrow::RawPtr(Mutability::Mut)) => "&raw mut ",
+            // some of these could be represented via `as` casts, but that's not too nice and
+            // handling everything as a prefix expr makes the `(` and `)` insertion easier
+            Adjust::Pointer(cast) if config.adjustment_hints == AdjustmentHints::Always => {
+                match cast {
+                    PointerCast::ReifyFnPointer => "<fn-item-to-fn-pointer>",
+                    PointerCast::UnsafeFnPointer => "<safe-fn-pointer-to-unsafe-fn-pointer>",
+                    PointerCast::ClosureFnPointer(Safety::Unsafe) => {
+                        "<closure-to-unsafe-fn-pointer>"
+                    }
+                    PointerCast::ClosureFnPointer(Safety::Safe) => "<closure-to-fn-pointer>",
+                    PointerCast::MutToConstPointer => "<mut-ptr-to-const-ptr>",
+                    PointerCast::ArrayToPointer => "<array-ptr-to-element-ptr>",
+                    PointerCast::Unsize => "<unsize>",
+                }
+            }
+            _ => continue,
+        };
+        acc.push(InlayHint {
+            range: expr.syntax().text_range(),
+            kind: InlayKind::AdjustmentHint,
+            label: text.into(),
+            tooltip: None,
+        });
+    }
+    if needs_parens {
+        acc.push(InlayHint {
+            range: expr.syntax().text_range(),
+            kind: InlayKind::AdjustmentHintClosingParenthesis,
+            label: ")".into(),
+            tooltip: None,
+        });
+    }
+    Some(())
+}
+
+fn chaining_hints(
+    acc: &mut Vec<InlayHint>,
+    sema: &Semantics<'_, RootDatabase>,
+    famous_defs: &FamousDefs<'_, '_>,
+    config: &InlayHintsConfig,
+    file_id: FileId,
+    expr: &ast::Expr,
+) -> Option<()> {
+    if !config.chaining_hints {
+        return None;
+    }
+
+    if matches!(expr, ast::Expr::RecordExpr(_)) {
+        return None;
+    }
+
+    let descended = sema.descend_node_into_attributes(expr.clone()).pop();
+    let desc_expr = descended.as_ref().unwrap_or(expr);
+
+    let mut tokens = expr
+        .syntax()
+        .siblings_with_tokens(Direction::Next)
+        .filter_map(NodeOrToken::into_token)
+        .filter(|t| match t.kind() {
+            SyntaxKind::WHITESPACE if !t.text().contains('\n') => false,
+            SyntaxKind::COMMENT => false,
+            _ => true,
+        });
+
+    // Chaining can be defined as an expression whose next sibling tokens are newline and dot
+    // Ignoring extra whitespace and comments
+    let next = tokens.next()?.kind();
+    if next == SyntaxKind::WHITESPACE {
+        let mut next_next = tokens.next()?.kind();
+        while next_next == SyntaxKind::WHITESPACE {
+            next_next = tokens.next()?.kind();
+        }
+        if next_next == T![.] {
+            let ty = sema.type_of_expr(desc_expr)?.original;
+            if ty.is_unknown() {
+                return None;
+            }
+            if matches!(expr, ast::Expr::PathExpr(_)) {
+                if let Some(hir::Adt::Struct(st)) = ty.as_adt() {
+                    if st.fields(sema.db).is_empty() {
+                        return None;
+                    }
+                }
+            }
+            acc.push(InlayHint {
+                range: expr.syntax().text_range(),
+                kind: InlayKind::ChainingHint,
+                label: hint_iterator(sema, &famous_defs, config, &ty)
+                    .unwrap_or_else(|| ty.display_truncated(sema.db, config.max_length).to_string())
+                    .into(),
+                tooltip: Some(InlayTooltip::HoverRanged(file_id, expr.syntax().text_range())),
+            });
+        }
+    }
+    Some(())
+}
+
+fn param_name_hints(
+    acc: &mut Vec<InlayHint>,
+    sema: &Semantics<'_, RootDatabase>,
+    config: &InlayHintsConfig,
+    expr: ast::Expr,
+) -> Option<()> {
+    if !config.parameter_hints {
+        return None;
+    }
+
+    let (callable, arg_list) = get_callable(sema, &expr)?;
+    let hints = callable
+        .params(sema.db)
+        .into_iter()
+        .zip(arg_list.args())
+        .filter_map(|((param, _ty), arg)| {
+            // Only annotate hints for expressions that exist in the original file
+            let range = sema.original_range_opt(arg.syntax())?;
+            let (param_name, name_syntax) = match param.as_ref()? {
+                Either::Left(pat) => ("self".to_string(), pat.name()),
+                Either::Right(pat) => match pat {
+                    ast::Pat::IdentPat(it) => (it.name()?.to_string(), it.name()),
+                    _ => return None,
+                },
+            };
+            Some((name_syntax, param_name, arg, range))
+        })
+        .filter(|(_, param_name, arg, _)| {
+            !should_hide_param_name_hint(sema, &callable, param_name, arg)
+        })
+        .map(|(param, param_name, _, FileRange { range, .. })| {
+            let mut tooltip = None;
+            if let Some(name) = param {
+                if let hir::CallableKind::Function(f) = callable.kind() {
+                    // assert the file is cached so we can map out of macros
+                    if let Some(_) = sema.source(f) {
+                        tooltip = sema.original_range_opt(name.syntax());
+                    }
+                }
+            }
+
+            InlayHint {
+                range,
+                kind: InlayKind::ParameterHint,
+                label: param_name.into(),
+                tooltip: tooltip.map(|it| InlayTooltip::HoverOffset(it.file_id, it.range.start())),
+            }
+        });
+
+    acc.extend(hints);
+    Some(())
+}
+
+fn binding_mode_hints(
+    acc: &mut Vec<InlayHint>,
+    sema: &Semantics<'_, RootDatabase>,
+    config: &InlayHintsConfig,
+    pat: &ast::Pat,
+) -> Option<()> {
+    if !config.binding_mode_hints {
+        return None;
+    }
+
+    let range = pat.syntax().text_range();
+    sema.pattern_adjustments(&pat).iter().for_each(|ty| {
+        let reference = ty.is_reference();
+        let mut_reference = ty.is_mutable_reference();
+        let r = match (reference, mut_reference) {
+            (true, true) => "&mut",
+            (true, false) => "&",
+            _ => return,
+        };
+        acc.push(InlayHint {
+            range,
+            kind: InlayKind::BindingModeHint,
+            label: r.to_string().into(),
+            tooltip: Some(InlayTooltip::String("Inferred binding mode".into())),
+        });
+    });
+    match pat {
+        ast::Pat::IdentPat(pat) if pat.ref_token().is_none() && pat.mut_token().is_none() => {
+            let bm = sema.binding_mode_of_pat(pat)?;
+            let bm = match bm {
+                hir::BindingMode::Move => return None,
+                hir::BindingMode::Ref(Mutability::Mut) => "ref mut",
+                hir::BindingMode::Ref(Mutability::Shared) => "ref",
+            };
+            acc.push(InlayHint {
+                range,
+                kind: InlayKind::BindingModeHint,
+                label: bm.to_string().into(),
+                tooltip: Some(InlayTooltip::String("Inferred binding mode".into())),
+            });
+        }
+        _ => (),
+    }
+
+    Some(())
+}
+
+fn bind_pat_hints(
+    acc: &mut Vec<InlayHint>,
+    sema: &Semantics<'_, RootDatabase>,
+    config: &InlayHintsConfig,
+    file_id: FileId,
+    pat: &ast::IdentPat,
+) -> Option<()> {
+    if !config.type_hints {
+        return None;
+    }
+
+    let descended = sema.descend_node_into_attributes(pat.clone()).pop();
+    let desc_pat = descended.as_ref().unwrap_or(pat);
+    let ty = sema.type_of_pat(&desc_pat.clone().into())?.original;
+
+    if should_not_display_type_hint(sema, config, pat, &ty) {
+        return None;
+    }
+
+    let krate = sema.scope(desc_pat.syntax())?.krate();
+    let famous_defs = FamousDefs(sema, krate);
+    let label = hint_iterator(sema, &famous_defs, config, &ty);
+
+    let label = match label {
+        Some(label) => label,
+        None => {
+            let ty_name = ty.display_truncated(sema.db, config.max_length).to_string();
+            if config.hide_named_constructor_hints
+                && is_named_constructor(sema, pat, &ty_name).is_some()
+            {
+                return None;
+            }
+            ty_name
+        }
+    };
+
+    acc.push(InlayHint {
+        range: match pat.name() {
+            Some(name) => name.syntax().text_range(),
+            None => pat.syntax().text_range(),
+        },
+        kind: InlayKind::TypeHint,
+        label: label.into(),
+        tooltip: pat
+            .name()
+            .map(|it| it.syntax().text_range())
+            .map(|it| InlayTooltip::HoverRanged(file_id, it)),
+    });
+
+    Some(())
+}
+
+fn is_named_constructor(
+    sema: &Semantics<'_, RootDatabase>,
+    pat: &ast::IdentPat,
+    ty_name: &str,
+) -> Option<()> {
+    let let_node = pat.syntax().parent()?;
+    let expr = match_ast! {
+        match let_node {
+            ast::LetStmt(it) => it.initializer(),
+            ast::LetExpr(it) => it.expr(),
+            _ => None,
+        }
+    }?;
+
+    let expr = sema.descend_node_into_attributes(expr.clone()).pop().unwrap_or(expr);
+    // unwrap postfix expressions
+    let expr = match expr {
+        ast::Expr::TryExpr(it) => it.expr(),
+        ast::Expr::AwaitExpr(it) => it.expr(),
+        expr => Some(expr),
+    }?;
+    let expr = match expr {
+        ast::Expr::CallExpr(call) => match call.expr()? {
+            ast::Expr::PathExpr(path) => path,
+            _ => return None,
+        },
+        ast::Expr::PathExpr(path) => path,
+        _ => return None,
+    };
+    let path = expr.path()?;
+
+    let callable = sema.type_of_expr(&ast::Expr::PathExpr(expr))?.original.as_callable(sema.db);
+    let callable_kind = callable.map(|it| it.kind());
+    let qual_seg = match callable_kind {
+        Some(hir::CallableKind::Function(_) | hir::CallableKind::TupleEnumVariant(_)) => {
+            path.qualifier()?.segment()
+        }
+        _ => path.segment(),
+    }?;
+
+    let ctor_name = match qual_seg.kind()? {
+        ast::PathSegmentKind::Name(name_ref) => {
+            match qual_seg.generic_arg_list().map(|it| it.generic_args()) {
+                Some(generics) => format!("{}<{}>", name_ref, generics.format(", ")),
+                None => name_ref.to_string(),
+            }
+        }
+        ast::PathSegmentKind::Type { type_ref: Some(ty), trait_ref: None } => ty.to_string(),
+        _ => return None,
+    };
+    (ctor_name == ty_name).then(|| ())
+}
+
 /// Checks if the type is an Iterator from std::iter and replaces its hint with an `impl Iterator<Item = Ty>`.
 fn hint_iterator(
     sema: &Semantics<'_, RootDatabase>,
@@ -320,56 +1041,320 @@ fn hint_iterator(
     None
 }
 
+fn pat_is_enum_variant(db: &RootDatabase, bind_pat: &ast::IdentPat, pat_ty: &hir::Type) -> bool {
+    if let Some(hir::Adt::Enum(enum_data)) = pat_ty.as_adt() {
+        let pat_text = bind_pat.to_string();
+        enum_data
+            .variants(db)
+            .into_iter()
+            .map(|variant| variant.name(db).to_smol_str())
+            .any(|enum_name| enum_name == pat_text)
+    } else {
+        false
+    }
+}
+
+fn should_not_display_type_hint(
+    sema: &Semantics<'_, RootDatabase>,
+    config: &InlayHintsConfig,
+    bind_pat: &ast::IdentPat,
+    pat_ty: &hir::Type,
+) -> bool {
+    let db = sema.db;
+
+    if pat_ty.is_unknown() {
+        return true;
+    }
+
+    if let Some(hir::Adt::Struct(s)) = pat_ty.as_adt() {
+        if s.fields(db).is_empty() && s.name(db).to_smol_str() == bind_pat.to_string() {
+            return true;
+        }
+    }
+
+    if config.hide_closure_initialization_hints {
+        if let Some(parent) = bind_pat.syntax().parent() {
+            if let Some(it) = ast::LetStmt::cast(parent.clone()) {
+                if let Some(ast::Expr::ClosureExpr(closure)) = it.initializer() {
+                    if closure_has_block_body(&closure) {
+                        return true;
+                    }
+                }
+            }
+        }
+    }
+
+    for node in bind_pat.syntax().ancestors() {
+        match_ast! {
+            match node {
+                ast::LetStmt(it) => return it.ty().is_some(),
+                // FIXME: We might wanna show type hints in parameters for non-top level patterns as well
+                ast::Param(it) => return it.ty().is_some(),
+                ast::MatchArm(_) => return pat_is_enum_variant(db, bind_pat, pat_ty),
+                ast::LetExpr(_) => return pat_is_enum_variant(db, bind_pat, pat_ty),
+                ast::IfExpr(_) => return false,
+                ast::WhileExpr(_) => return false,
+                ast::ForExpr(it) => {
+                    // We *should* display hint only if user provided "in {expr}" and we know the type of expr (and it's not unit).
+                    // Type of expr should be iterable.
+                    return it.in_token().is_none() ||
+                        it.iterable()
+                            .and_then(|iterable_expr| sema.type_of_expr(&iterable_expr))
+                            .map(TypeInfo::original)
+                            .map_or(true, |iterable_ty| iterable_ty.is_unknown() || iterable_ty.is_unit())
+                },
+                _ => (),
+            }
+        }
+    }
+    false
+}
+
 fn closure_has_block_body(closure: &ast::ClosureExpr) -> bool {
     matches!(closure.body(), Some(ast::Expr::BlockExpr(_)))
 }
 
-#[cfg(test)]
-mod tests {
-    use expect_test::Expect;
-    use itertools::Itertools;
-    use test_utils::extract_annotations;
+fn should_hide_param_name_hint(
+    sema: &Semantics<'_, RootDatabase>,
+    callable: &hir::Callable,
+    param_name: &str,
+    argument: &ast::Expr,
+) -> bool {
+    // These are to be tested in the `parameter_hint_heuristics` test
+    // hide when:
+    // - the parameter name is a suffix of the function's name
+    // - the argument is a qualified constructing or call expression where the qualifier is an ADT
+    // - exact argument<->parameter match(ignoring leading underscore) or parameter is a prefix/suffix
+    //   of argument with _ splitting it off
+    // - param starts with `ra_fixture`
+    // - param is a well known name in a unary function
 
-    use crate::inlay_hints::AdjustmentHints;
-    use crate::{fixture, inlay_hints::InlayHintsConfig, LifetimeElisionHints};
+    let param_name = param_name.trim_start_matches('_');
+    if param_name.is_empty() {
+        return true;
+    }
 
-    use super::ClosureReturnTypeHints;
+    if matches!(argument, ast::Expr::PrefixExpr(prefix) if prefix.op_kind() == Some(UnaryOp::Not)) {
+        return false;
+    }
 
-    pub(super) const DISABLED_CONFIG: InlayHintsConfig = InlayHintsConfig {
-        render_colons: false,
-        type_hints: false,
-        parameter_hints: false,
-        chaining_hints: false,
-        lifetime_elision_hints: LifetimeElisionHints::Never,
-        closure_return_type_hints: ClosureReturnTypeHints::Never,
-        adjustment_hints: AdjustmentHints::Never,
-        binding_mode_hints: false,
-        hide_named_constructor_hints: false,
-        hide_closure_initialization_hints: false,
-        param_names_for_lifetime_elision_hints: false,
-        max_length: None,
-        closing_brace_hints_min_lines: None,
+    let fn_name = match callable.kind() {
+        hir::CallableKind::Function(it) => Some(it.name(sema.db).to_smol_str()),
+        _ => None,
     };
-    pub(super) const TEST_CONFIG: InlayHintsConfig = InlayHintsConfig {
-        type_hints: true,
-        parameter_hints: true,
-        chaining_hints: true,
-        closure_return_type_hints: ClosureReturnTypeHints::WithBlock,
-        binding_mode_hints: true,
-        lifetime_elision_hints: LifetimeElisionHints::Always,
-        ..DISABLED_CONFIG
+    let fn_name = fn_name.as_deref();
+    is_param_name_suffix_of_fn_name(param_name, callable, fn_name)
+        || is_argument_similar_to_param_name(argument, param_name)
+        || param_name.starts_with("ra_fixture")
+        || (callable.n_params() == 1 && is_obvious_param(param_name))
+        || is_adt_constructor_similar_to_param_name(sema, argument, param_name)
+}
+
+fn is_argument_similar_to_param_name(argument: &ast::Expr, param_name: &str) -> bool {
+    // check whether param_name and argument are the same or
+    // whether param_name is a prefix/suffix of argument(split at `_`)
+    let argument = match get_string_representation(argument) {
+        Some(argument) => argument,
+        None => return false,
     };
 
-    #[track_caller]
-    pub(super) fn check(ra_fixture: &str) {
-        check_with_config(TEST_CONFIG, ra_fixture);
-    }
+    // std is honestly too panic happy...
+    let str_split_at = |str: &str, at| str.is_char_boundary(at).then(|| argument.split_at(at));
 
-    #[track_caller]
-    pub(super) fn check_with_config(config: InlayHintsConfig, ra_fixture: &str) {
-        let (analysis, file_id) = fixture::file(ra_fixture);
-        let mut expected = extract_annotations(&*analysis.file_text(file_id).unwrap());
-        let inlay_hints = analysis.inlay_hints(&config, file_id, None).unwrap();
+    let param_name = param_name.trim_start_matches('_');
+    let argument = argument.trim_start_matches('_');
+
+    match str_split_at(argument, param_name.len()) {
+        Some((prefix, rest)) if prefix.eq_ignore_ascii_case(param_name) => {
+            return rest.is_empty() || rest.starts_with('_');
+        }
+        _ => (),
+    }
+    match argument.len().checked_sub(param_name.len()).and_then(|at| str_split_at(argument, at)) {
+        Some((rest, suffix)) if param_name.eq_ignore_ascii_case(suffix) => {
+            return rest.is_empty() || rest.ends_with('_');
+        }
+        _ => (),
+    }
+    false
+}
+
+/// Hide the parameter name of a unary function if it is a `_` - prefixed suffix of the function's name, or equal.
+///
+/// `fn strip_suffix(suffix)` will be hidden.
+/// `fn stripsuffix(suffix)` will not be hidden.
+fn is_param_name_suffix_of_fn_name(
+    param_name: &str,
+    callable: &Callable,
+    fn_name: Option<&str>,
+) -> bool {
+    match (callable.n_params(), fn_name) {
+        (1, Some(function)) => {
+            function == param_name
+                || function
+                    .len()
+                    .checked_sub(param_name.len())
+                    .and_then(|at| function.is_char_boundary(at).then(|| function.split_at(at)))
+                    .map_or(false, |(prefix, suffix)| {
+                        suffix.eq_ignore_ascii_case(param_name) && prefix.ends_with('_')
+                    })
+        }
+        _ => false,
+    }
+}
+
+fn is_adt_constructor_similar_to_param_name(
+    sema: &Semantics<'_, RootDatabase>,
+    argument: &ast::Expr,
+    param_name: &str,
+) -> bool {
+    let path = match argument {
+        ast::Expr::CallExpr(c) => c.expr().and_then(|e| match e {
+            ast::Expr::PathExpr(p) => p.path(),
+            _ => None,
+        }),
+        ast::Expr::PathExpr(p) => p.path(),
+        ast::Expr::RecordExpr(r) => r.path(),
+        _ => return false,
+    };
+    let path = match path {
+        Some(it) => it,
+        None => return false,
+    };
+    (|| match sema.resolve_path(&path)? {
+        hir::PathResolution::Def(hir::ModuleDef::Adt(_)) => {
+            Some(to_lower_snake_case(&path.segment()?.name_ref()?.text()) == param_name)
+        }
+        hir::PathResolution::Def(hir::ModuleDef::Function(_) | hir::ModuleDef::Variant(_)) => {
+            if to_lower_snake_case(&path.segment()?.name_ref()?.text()) == param_name {
+                return Some(true);
+            }
+            let qual = path.qualifier()?;
+            match sema.resolve_path(&qual)? {
+                hir::PathResolution::Def(hir::ModuleDef::Adt(_)) => {
+                    Some(to_lower_snake_case(&qual.segment()?.name_ref()?.text()) == param_name)
+                }
+                _ => None,
+            }
+        }
+        _ => None,
+    })()
+    .unwrap_or(false)
+}
+
+fn get_string_representation(expr: &ast::Expr) -> Option<String> {
+    match expr {
+        ast::Expr::MethodCallExpr(method_call_expr) => {
+            let name_ref = method_call_expr.name_ref()?;
+            match name_ref.text().as_str() {
+                "clone" | "as_ref" => method_call_expr.receiver().map(|rec| rec.to_string()),
+                name_ref => Some(name_ref.to_owned()),
+            }
+        }
+        ast::Expr::MacroExpr(macro_expr) => {
+            Some(macro_expr.macro_call()?.path()?.segment()?.to_string())
+        }
+        ast::Expr::FieldExpr(field_expr) => Some(field_expr.name_ref()?.to_string()),
+        ast::Expr::PathExpr(path_expr) => Some(path_expr.path()?.segment()?.to_string()),
+        ast::Expr::PrefixExpr(prefix_expr) => get_string_representation(&prefix_expr.expr()?),
+        ast::Expr::RefExpr(ref_expr) => get_string_representation(&ref_expr.expr()?),
+        ast::Expr::CastExpr(cast_expr) => get_string_representation(&cast_expr.expr()?),
+        _ => None,
+    }
+}
+
+fn is_obvious_param(param_name: &str) -> bool {
+    // avoid displaying hints for common functions like map, filter, etc.
+    // or other obvious words used in std
+    let is_obvious_param_name =
+        matches!(param_name, "predicate" | "value" | "pat" | "rhs" | "other");
+    param_name.len() == 1 || is_obvious_param_name
+}
+
+fn get_callable(
+    sema: &Semantics<'_, RootDatabase>,
+    expr: &ast::Expr,
+) -> Option<(hir::Callable, ast::ArgList)> {
+    match expr {
+        ast::Expr::CallExpr(expr) => {
+            let descended = sema.descend_node_into_attributes(expr.clone()).pop();
+            let expr = descended.as_ref().unwrap_or(expr);
+            sema.type_of_expr(&expr.expr()?)?.original.as_callable(sema.db).zip(expr.arg_list())
+        }
+        ast::Expr::MethodCallExpr(expr) => {
+            let descended = sema.descend_node_into_attributes(expr.clone()).pop();
+            let expr = descended.as_ref().unwrap_or(expr);
+            sema.resolve_method_call_as_callable(expr).zip(expr.arg_list())
+        }
+        _ => None,
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use expect_test::{expect, Expect};
+    use itertools::Itertools;
+    use syntax::{TextRange, TextSize};
+    use test_utils::extract_annotations;
+
+    use crate::inlay_hints::AdjustmentHints;
+    use crate::{fixture, inlay_hints::InlayHintsConfig, LifetimeElisionHints};
+
+    use super::ClosureReturnTypeHints;
+
+    const DISABLED_CONFIG: InlayHintsConfig = InlayHintsConfig {
+        render_colons: false,
+        type_hints: false,
+        parameter_hints: false,
+        chaining_hints: false,
+        lifetime_elision_hints: LifetimeElisionHints::Never,
+        closure_return_type_hints: ClosureReturnTypeHints::Never,
+        adjustment_hints: AdjustmentHints::Never,
+        binding_mode_hints: false,
+        hide_named_constructor_hints: false,
+        hide_closure_initialization_hints: false,
+        param_names_for_lifetime_elision_hints: false,
+        max_length: None,
+        closing_brace_hints_min_lines: None,
+    };
+    const TEST_CONFIG: InlayHintsConfig = InlayHintsConfig {
+        type_hints: true,
+        parameter_hints: true,
+        chaining_hints: true,
+        closure_return_type_hints: ClosureReturnTypeHints::WithBlock,
+        binding_mode_hints: true,
+        lifetime_elision_hints: LifetimeElisionHints::Always,
+        ..DISABLED_CONFIG
+    };
+
+    #[track_caller]
+    fn check(ra_fixture: &str) {
+        check_with_config(TEST_CONFIG, ra_fixture);
+    }
+
+    #[track_caller]
+    fn check_params(ra_fixture: &str) {
+        check_with_config(
+            InlayHintsConfig { parameter_hints: true, ..DISABLED_CONFIG },
+            ra_fixture,
+        );
+    }
+
+    #[track_caller]
+    fn check_types(ra_fixture: &str) {
+        check_with_config(InlayHintsConfig { type_hints: true, ..DISABLED_CONFIG }, ra_fixture);
+    }
+
+    #[track_caller]
+    fn check_chains(ra_fixture: &str) {
+        check_with_config(InlayHintsConfig { chaining_hints: true, ..DISABLED_CONFIG }, ra_fixture);
+    }
+
+    #[track_caller]
+    fn check_with_config(config: InlayHintsConfig, ra_fixture: &str) {
+        let (analysis, file_id) = fixture::file(ra_fixture);
+        let mut expected = extract_annotations(&*analysis.file_text(file_id).unwrap());
+        let inlay_hints = analysis.inlay_hints(&config, file_id, None).unwrap();
         let actual = inlay_hints
             .into_iter()
             .map(|it| (it.range, it.label.to_string()))
@@ -381,7 +1366,7 @@ mod tests {
     }
 
     #[track_caller]
-    pub(super) fn check_expect(config: InlayHintsConfig, ra_fixture: &str, expect: Expect) {
+    fn check_expect(config: InlayHintsConfig, ra_fixture: &str, expect: Expect) {
         let (analysis, file_id) = fixture::file(ra_fixture);
         let inlay_hints = analysis.inlay_hints(&config, file_id, None).unwrap();
         expect.assert_debug_eq(&inlay_hints)
@@ -398,4 +1383,1749 @@ fn main() {
 }"#,
         );
     }
+
+    // Parameter hint tests
+
+    #[test]
+    fn param_hints_only() {
+        check_params(
+            r#"
+fn foo(a: i32, b: i32) -> i32 { a + b }
+fn main() {
+    let _x = foo(
+        4,
+      //^ a
+        4,
+      //^ b
+    );
+}"#,
+        );
+    }
+
+    #[test]
+    fn param_hints_on_closure() {
+        check_params(
+            r#"
+fn main() {
+    let clo = |a: u8, b: u8| a + b;
+    clo(
+        1,
+      //^ a
+        2,
+      //^ b
+    );
+}
+            "#,
+        );
+    }
+
+    #[test]
+    fn param_name_similar_to_fn_name_still_hints() {
+        check_params(
+            r#"
+fn max(x: i32, y: i32) -> i32 { x + y }
+fn main() {
+    let _x = max(
+        4,
+      //^ x
+        4,
+      //^ y
+    );
+}"#,
+        );
+    }
+
+    #[test]
+    fn param_name_similar_to_fn_name() {
+        check_params(
+            r#"
+fn param_with_underscore(with_underscore: i32) -> i32 { with_underscore }
+fn main() {
+    let _x = param_with_underscore(
+        4,
+    );
+}"#,
+        );
+        check_params(
+            r#"
+fn param_with_underscore(underscore: i32) -> i32 { underscore }
+fn main() {
+    let _x = param_with_underscore(
+        4,
+    );
+}"#,
+        );
+    }
+
+    #[test]
+    fn param_name_same_as_fn_name() {
+        check_params(
+            r#"
+fn foo(foo: i32) -> i32 { foo }
+fn main() {
+    let _x = foo(
+        4,
+    );
+}"#,
+        );
+    }
+
+    #[test]
+    fn never_hide_param_when_multiple_params() {
+        check_params(
+            r#"
+fn foo(foo: i32, bar: i32) -> i32 { bar + baz }
+fn main() {
+    let _x = foo(
+        4,
+      //^ foo
+        8,
+      //^ bar
+    );
+}"#,
+        );
+    }
+
+    #[test]
+    fn param_hints_look_through_as_ref_and_clone() {
+        check_params(
+            r#"
+fn foo(bar: i32, baz: f32) {}
+
+fn main() {
+    let bar = 3;
+    let baz = &"baz";
+    let fez = 1.0;
+    foo(bar.clone(), bar.clone());
+                   //^^^^^^^^^^^ baz
+    foo(bar.as_ref(), bar.as_ref());
+                    //^^^^^^^^^^^^ baz
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn self_param_hints() {
+        check_params(
+            r#"
+struct Foo;
+
+impl Foo {
+    fn foo(self: Self) {}
+    fn bar(self: &Self) {}
+}
+
+fn main() {
+    Foo::foo(Foo);
+           //^^^ self
+    Foo::bar(&Foo);
+           //^^^^ self
+}
+"#,
+        )
+    }
+
+    #[test]
+    fn param_name_hints_show_for_literals() {
+        check_params(
+            r#"pub fn test(a: i32, b: i32) -> [i32; 2] { [a, b] }
+fn main() {
+    test(
+        0xa_b,
+      //^^^^^ a
+        0xa_b,
+      //^^^^^ b
+    );
+}"#,
+        )
+    }
+
+    #[test]
+    fn function_call_parameter_hint() {
+        check_params(
+            r#"
+//- minicore: option
+struct FileId {}
+struct SmolStr {}
+
+struct TextRange {}
+struct SyntaxKind {}
+struct NavigationTarget {}
+
+struct Test {}
+
+impl Test {
+    fn method(&self, mut param: i32) -> i32 { param * 2 }
+
+    fn from_syntax(
+        file_id: FileId,
+        name: SmolStr,
+        focus_range: Option<TextRange>,
+        full_range: TextRange,
+        kind: SyntaxKind,
+        docs: Option<String>,
+    ) -> NavigationTarget {
+        NavigationTarget {}
+    }
+}
+
+fn test_func(mut foo: i32, bar: i32, msg: &str, _: i32, last: i32) -> i32 {
+    foo + bar
+}
+
+fn main() {
+    let not_literal = 1;
+    let _: i32 = test_func(1,    2,      "hello", 3,  not_literal);
+                         //^ foo ^ bar   ^^^^^^^ msg  ^^^^^^^^^^^ last
+    let t: Test = Test {};
+    t.method(123);
+           //^^^ param
+    Test::method(&t,      3456);
+               //^^ self  ^^^^ param
+    Test::from_syntax(
+        FileId {},
+        "impl".into(),
+      //^^^^^^^^^^^^^ name
+        None,
+      //^^^^ focus_range
+        TextRange {},
+      //^^^^^^^^^^^^ full_range
+        SyntaxKind {},
+      //^^^^^^^^^^^^^ kind
+        None,
+      //^^^^ docs
+    );
+}"#,
+        );
+    }
+
+    #[test]
+    fn parameter_hint_heuristics() {
+        check_params(
+            r#"
+fn check(ra_fixture_thing: &str) {}
+
+fn map(f: i32) {}
+fn filter(predicate: i32) {}
+
+fn strip_suffix(suffix: &str) {}
+fn stripsuffix(suffix: &str) {}
+fn same(same: u32) {}
+fn same2(_same2: u32) {}
+
+fn enum_matches_param_name(completion_kind: CompletionKind) {}
+
+fn foo(param: u32) {}
+fn bar(param_eter: u32) {}
+
+enum CompletionKind {
+    Keyword,
+}
+
+fn non_ident_pat((a, b): (u32, u32)) {}
+
+fn main() {
+    const PARAM: u32 = 0;
+    foo(PARAM);
+    foo(!PARAM);
+     // ^^^^^^ param
+    check("");
+
+    map(0);
+    filter(0);
+
+    strip_suffix("");
+    stripsuffix("");
+              //^^ suffix
+    same(0);
+    same2(0);
+
+    enum_matches_param_name(CompletionKind::Keyword);
+
+    let param = 0;
+    foo(param);
+    foo(param as _);
+    let param_end = 0;
+    foo(param_end);
+    let start_param = 0;
+    foo(start_param);
+    let param2 = 0;
+    foo(param2);
+      //^^^^^^ param
+
+    macro_rules! param {
+        () => {};
+    };
+    foo(param!());
+
+    let param_eter = 0;
+    bar(param_eter);
+    let param_eter_end = 0;
+    bar(param_eter_end);
+    let start_param_eter = 0;
+    bar(start_param_eter);
+    let param_eter2 = 0;
+    bar(param_eter2);
+      //^^^^^^^^^^^ param_eter
+
+    non_ident_pat((0, 0));
+}"#,
+        );
+    }
+
+    // Type-Hint tests
+
+    #[test]
+    fn type_hints_only() {
+        check_types(
+            r#"
+fn foo(a: i32, b: i32) -> i32 { a + b }
+fn main() {
+    let _x = foo(4, 4);
+      //^^ i32
+}"#,
+        );
+    }
+
+    #[test]
+    fn type_hints_bindings_after_at() {
+        check_types(
+            r#"
+//- minicore: option
+fn main() {
+    let ref foo @ bar @ ref mut baz = 0;
+          //^^^ &i32
+                //^^^ i32
+                              //^^^ &mut i32
+    let [x @ ..] = [0];
+       //^ [i32; 1]
+    if let x @ Some(_) = Some(0) {}
+         //^ Option<i32>
+    let foo @ (bar, baz) = (3, 3);
+      //^^^ (i32, i32)
+             //^^^ i32
+                  //^^^ i32
+}"#,
+        );
+    }
+
+    #[test]
+    fn default_generic_types_should_not_be_displayed() {
+        check(
+            r#"
+struct Test<K, T = u8> { k: K, t: T }
+
+fn main() {
+    let zz = Test { t: 23u8, k: 33 };
+      //^^ Test<i32>
+    let zz_ref = &zz;
+      //^^^^^^ &Test<i32>
+    let test = || zz;
+      //^^^^ || -> Test<i32>
+}"#,
+        );
+    }
+
+    #[test]
+    fn shorten_iterators_in_associated_params() {
+        check_types(
+            r#"
+//- minicore: iterators
+use core::iter;
+
+pub struct SomeIter<T> {}
+
+impl<T> SomeIter<T> {
+    pub fn new() -> Self { SomeIter {} }
+    pub fn push(&mut self, t: T) {}
+}
+
+impl<T> Iterator for SomeIter<T> {
+    type Item = T;
+    fn next(&mut self) -> Option<Self::Item> {
+        None
+    }
+}
+
+fn main() {
+    let mut some_iter = SomeIter::new();
+          //^^^^^^^^^ SomeIter<Take<Repeat<i32>>>
+      some_iter.push(iter::repeat(2).take(2));
+    let iter_of_iters = some_iter.take(2);
+      //^^^^^^^^^^^^^ impl Iterator<Item = impl Iterator<Item = i32>>
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn iterator_hint_regression_issue_12674() {
+        // Ensure we don't crash while solving the projection type of iterators.
+        check_expect(
+            InlayHintsConfig { chaining_hints: true, ..DISABLED_CONFIG },
+            r#"
+//- minicore: iterators
+struct S<T>(T);
+impl<T> S<T> {
+    fn iter(&self) -> Iter<'_, T> { loop {} }
+}
+struct Iter<'a, T: 'a>(&'a T);
+impl<'a, T> Iterator for Iter<'a, T> {
+    type Item = &'a T;
+    fn next(&mut self) -> Option<Self::Item> { loop {} }
+}
+struct Container<'a> {
+    elements: S<&'a str>,
+}
+struct SliceIter<'a, T>(&'a T);
+impl<'a, T> Iterator for SliceIter<'a, T> {
+    type Item = &'a T;
+    fn next(&mut self) -> Option<Self::Item> { loop {} }
+}
+
+fn main(a: SliceIter<'_, Container>) {
+    a
+    .filter_map(|c| Some(c.elements.iter().filter_map(|v| Some(v))))
+    .map(|e| e);
+}
+            "#,
+            expect![[r#"
+                [
+                    InlayHint {
+                        range: 484..554,
+                        kind: ChainingHint,
+                        label: [
+                            "impl Iterator<Item = impl Iterator<Item = &&str>>",
+                        ],
+                        tooltip: Some(
+                            HoverRanged(
+                                FileId(
+                                    0,
+                                ),
+                                484..554,
+                            ),
+                        ),
+                    },
+                    InlayHint {
+                        range: 484..485,
+                        kind: ChainingHint,
+                        label: [
+                            "SliceIter<Container>",
+                        ],
+                        tooltip: Some(
+                            HoverRanged(
+                                FileId(
+                                    0,
+                                ),
+                                484..485,
+                            ),
+                        ),
+                    },
+                ]
+            "#]],
+        );
+    }
+
+    #[test]
+    fn infer_call_method_return_associated_types_with_generic() {
+        check_types(
+            r#"
+            pub trait Default {
+                fn default() -> Self;
+            }
+            pub trait Foo {
+                type Bar: Default;
+            }
+
+            pub fn quux<T: Foo>() -> T::Bar {
+                let y = Default::default();
+                  //^ <T as Foo>::Bar
+
+                y
+            }
+            "#,
+        );
+    }
+
+    #[test]
+    fn fn_hints() {
+        check_types(
+            r#"
+//- minicore: fn, sized
+fn foo() -> impl Fn() { loop {} }
+fn foo1() -> impl Fn(f64) { loop {} }
+fn foo2() -> impl Fn(f64, f64) { loop {} }
+fn foo3() -> impl Fn(f64, f64) -> u32 { loop {} }
+fn foo4() -> &'static dyn Fn(f64, f64) -> u32 { loop {} }
+fn foo5() -> &'static dyn Fn(&'static dyn Fn(f64, f64) -> u32, f64) -> u32 { loop {} }
+fn foo6() -> impl Fn(f64, f64) -> u32 + Sized { loop {} }
+fn foo7() -> *const (impl Fn(f64, f64) -> u32 + Sized) { loop {} }
+
+fn main() {
+    let foo = foo();
+     // ^^^ impl Fn()
+    let foo = foo1();
+     // ^^^ impl Fn(f64)
+    let foo = foo2();
+     // ^^^ impl Fn(f64, f64)
+    let foo = foo3();
+     // ^^^ impl Fn(f64, f64) -> u32
+    let foo = foo4();
+     // ^^^ &dyn Fn(f64, f64) -> u32
+    let foo = foo5();
+     // ^^^ &dyn Fn(&dyn Fn(f64, f64) -> u32, f64) -> u32
+    let foo = foo6();
+     // ^^^ impl Fn(f64, f64) -> u32
+    let foo = foo7();
+     // ^^^ *const impl Fn(f64, f64) -> u32
+}
+"#,
+        )
+    }
+
+    #[test]
+    fn check_hint_range_limit() {
+        let fixture = r#"
+        //- minicore: fn, sized
+        fn foo() -> impl Fn() { loop {} }
+        fn foo1() -> impl Fn(f64) { loop {} }
+        fn foo2() -> impl Fn(f64, f64) { loop {} }
+        fn foo3() -> impl Fn(f64, f64) -> u32 { loop {} }
+        fn foo4() -> &'static dyn Fn(f64, f64) -> u32 { loop {} }
+        fn foo5() -> &'static dyn Fn(&'static dyn Fn(f64, f64) -> u32, f64) -> u32 { loop {} }
+        fn foo6() -> impl Fn(f64, f64) -> u32 + Sized { loop {} }
+        fn foo7() -> *const (impl Fn(f64, f64) -> u32 + Sized) { loop {} }
+
+        fn main() {
+            let foo = foo();
+            let foo = foo1();
+            let foo = foo2();
+             // ^^^ impl Fn(f64, f64)
+            let foo = foo3();
+             // ^^^ impl Fn(f64, f64) -> u32
+            let foo = foo4();
+            let foo = foo5();
+            let foo = foo6();
+            let foo = foo7();
+        }
+        "#;
+        let (analysis, file_id) = fixture::file(fixture);
+        let expected = extract_annotations(&*analysis.file_text(file_id).unwrap());
+        let inlay_hints = analysis
+            .inlay_hints(
+                &InlayHintsConfig { type_hints: true, ..DISABLED_CONFIG },
+                file_id,
+                Some(TextRange::new(TextSize::from(500), TextSize::from(600))),
+            )
+            .unwrap();
+        let actual =
+            inlay_hints.into_iter().map(|it| (it.range, it.label.to_string())).collect::<Vec<_>>();
+        assert_eq!(expected, actual, "\nExpected:\n{:#?}\n\nActual:\n{:#?}", expected, actual);
+    }
+
+    #[test]
+    fn fn_hints_ptr_rpit_fn_parentheses() {
+        check_types(
+            r#"
+//- minicore: fn, sized
+trait Trait {}
+
+fn foo1() -> *const impl Fn() { loop {} }
+fn foo2() -> *const (impl Fn() + Sized) { loop {} }
+fn foo3() -> *const (impl Fn() + ?Sized) { loop {} }
+fn foo4() -> *const (impl Sized + Fn()) { loop {} }
+fn foo5() -> *const (impl ?Sized + Fn()) { loop {} }
+fn foo6() -> *const (impl Fn() + Trait) { loop {} }
+fn foo7() -> *const (impl Fn() + Sized + Trait) { loop {} }
+fn foo8() -> *const (impl Fn() + ?Sized + Trait) { loop {} }
+fn foo9() -> *const (impl Fn() -> u8 + ?Sized) { loop {} }
+fn foo10() -> *const (impl Fn() + Sized + ?Sized) { loop {} }
+
+fn main() {
+    let foo = foo1();
+    //  ^^^ *const impl Fn()
+    let foo = foo2();
+    //  ^^^ *const impl Fn()
+    let foo = foo3();
+    //  ^^^ *const (impl Fn() + ?Sized)
+    let foo = foo4();
+    //  ^^^ *const impl Fn()
+    let foo = foo5();
+    //  ^^^ *const (impl Fn() + ?Sized)
+    let foo = foo6();
+    //  ^^^ *const (impl Fn() + Trait)
+    let foo = foo7();
+    //  ^^^ *const (impl Fn() + Trait)
+    let foo = foo8();
+    //  ^^^ *const (impl Fn() + Trait + ?Sized)
+    let foo = foo9();
+    //  ^^^ *const (impl Fn() -> u8 + ?Sized)
+    let foo = foo10();
+    //  ^^^ *const impl Fn()
+}
+"#,
+        )
+    }
+
+    #[test]
+    fn unit_structs_have_no_type_hints() {
+        check_types(
+            r#"
+//- minicore: result
+struct SyntheticSyntax;
+
+fn main() {
+    match Ok(()) {
+        Ok(_) => (),
+        Err(SyntheticSyntax) => (),
+    }
+}"#,
+        );
+    }
+
+    #[test]
+    fn let_statement() {
+        check_types(
+            r#"
+#[derive(PartialEq)]
+enum Option<T> { None, Some(T) }
+
+#[derive(PartialEq)]
+struct Test { a: Option<u32>, b: u8 }
+
+fn main() {
+    struct InnerStruct {}
+
+    let test = 54;
+      //^^^^ i32
+    let test: i32 = 33;
+    let mut test = 33;
+          //^^^^ i32
+    let _ = 22;
+    let test = "test";
+      //^^^^ &str
+    let test = InnerStruct {};
+      //^^^^ InnerStruct
+
+    let test = unresolved();
+
+    let test = (42, 'a');
+      //^^^^ (i32, char)
+    let (a,    (b,     (c,)) = (2, (3, (9.2,));
+       //^ i32  ^ i32   ^ f64
+    let &x = &92;
+       //^ i32
+}"#,
+        );
+    }
+
+    #[test]
+    fn if_expr() {
+        check_types(
+            r#"
+//- minicore: option
+struct Test { a: Option<u32>, b: u8 }
+
+fn main() {
+    let test = Some(Test { a: Some(3), b: 1 });
+      //^^^^ Option<Test>
+    if let None = &test {};
+    if let test = &test {};
+         //^^^^ &Option<Test>
+    if let Some(test) = &test {};
+              //^^^^ &Test
+    if let Some(Test { a,             b }) = &test {};
+                     //^ &Option<u32> ^ &u8
+    if let Some(Test { a: x,             b: y }) = &test {};
+                        //^ &Option<u32>    ^ &u8
+    if let Some(Test { a: Some(x),  b: y }) = &test {};
+                             //^ &u32  ^ &u8
+    if let Some(Test { a: None,  b: y }) = &test {};
+                                  //^ &u8
+    if let Some(Test { b: y, .. }) = &test {};
+                        //^ &u8
+    if test == None {}
+}"#,
+        );
+    }
+
+    #[test]
+    fn while_expr() {
+        check_types(
+            r#"
+//- minicore: option
+struct Test { a: Option<u32>, b: u8 }
+
+fn main() {
+    let test = Some(Test { a: Some(3), b: 1 });
+      //^^^^ Option<Test>
+    while let Some(Test { a: Some(x),  b: y }) = &test {};
+                                //^ &u32  ^ &u8
+}"#,
+        );
+    }
+
+    #[test]
+    fn match_arm_list() {
+        check_types(
+            r#"
+//- minicore: option
+struct Test { a: Option<u32>, b: u8 }
+
+fn main() {
+    match Some(Test { a: Some(3), b: 1 }) {
+        None => (),
+        test => (),
+      //^^^^ Option<Test>
+        Some(Test { a: Some(x), b: y }) => (),
+                          //^ u32  ^ u8
+        _ => {}
+    }
+}"#,
+        );
+    }
+
+    #[test]
+    fn complete_for_hint() {
+        check_types(
+            r#"
+//- minicore: iterator
+pub struct Vec<T> {}
+
+impl<T> Vec<T> {
+    pub fn new() -> Self { Vec {} }
+    pub fn push(&mut self, t: T) {}
+}
+
+impl<T> IntoIterator for Vec<T> {
+    type Item = T;
+    type IntoIter = IntoIter<T>;
+}
+
+struct IntoIter<T> {}
+
+impl<T> Iterator for IntoIter<T> {
+    type Item = T;
+}
+
+fn main() {
+    let mut data = Vec::new();
+          //^^^^ Vec<&str>
+    data.push("foo");
+    for i in data {
+      //^ &str
+      let z = i;
+        //^ &str
+    }
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn multi_dyn_trait_bounds() {
+        check_types(
+            r#"
+pub struct Vec<T> {}
+
+impl<T> Vec<T> {
+    pub fn new() -> Self { Vec {} }
+}
+
+pub struct Box<T> {}
+
+trait Display {}
+auto trait Sync {}
+
+fn main() {
+    // The block expression wrapping disables the constructor hint hiding logic
+    let _v = { Vec::<Box<&(dyn Display + Sync)>>::new() };
+      //^^ Vec<Box<&(dyn Display + Sync)>>
+    let _v = { Vec::<Box<*const (dyn Display + Sync)>>::new() };
+      //^^ Vec<Box<*const (dyn Display + Sync)>>
+    let _v = { Vec::<Box<dyn Display + Sync>>::new() };
+      //^^ Vec<Box<dyn Display + Sync>>
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn shorten_iterator_hints() {
+        check_types(
+            r#"
+//- minicore: iterators
+use core::iter;
+
+struct MyIter;
+
+impl Iterator for MyIter {
+    type Item = ();
+    fn next(&mut self) -> Option<Self::Item> {
+        None
+    }
+}
+
+fn main() {
+    let _x = MyIter;
+      //^^ MyIter
+    let _x = iter::repeat(0);
+      //^^ impl Iterator<Item = i32>
+    fn generic<T: Clone>(t: T) {
+        let _x = iter::repeat(t);
+          //^^ impl Iterator<Item = T>
+        let _chained = iter::repeat(t).take(10);
+          //^^^^^^^^ impl Iterator<Item = T>
+    }
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn skip_constructor_and_enum_type_hints() {
+        check_with_config(
+            InlayHintsConfig {
+                type_hints: true,
+                hide_named_constructor_hints: true,
+                ..DISABLED_CONFIG
+            },
+            r#"
+//- minicore: try, option
+use core::ops::ControlFlow;
+
+mod x {
+    pub mod y { pub struct Foo; }
+    pub struct Foo;
+    pub enum AnotherEnum {
+        Variant()
+    };
+}
+struct Struct;
+struct TupleStruct();
+
+impl Struct {
+    fn new() -> Self {
+        Struct
+    }
+    fn try_new() -> ControlFlow<(), Self> {
+        ControlFlow::Continue(Struct)
+    }
+}
+
+struct Generic<T>(T);
+impl Generic<i32> {
+    fn new() -> Self {
+        Generic(0)
+    }
+}
+
+enum Enum {
+    Variant(u32)
+}
+
+fn times2(value: i32) -> i32 {
+    2 * value
+}
+
+fn main() {
+    let enumb = Enum::Variant(0);
+
+    let strukt = x::Foo;
+    let strukt = x::y::Foo;
+    let strukt = Struct;
+    let strukt = Struct::new();
+
+    let tuple_struct = TupleStruct();
+
+    let generic0 = Generic::new();
+    //  ^^^^^^^^ Generic<i32>
+    let generic1 = Generic(0);
+    //  ^^^^^^^^ Generic<i32>
+    let generic2 = Generic::<i32>::new();
+    let generic3 = <Generic<i32>>::new();
+    let generic4 = Generic::<i32>(0);
+
+
+    let option = Some(0);
+    //  ^^^^^^ Option<i32>
+    let func = times2;
+    //  ^^^^ fn times2(i32) -> i32
+    let closure = |x: i32| x * 2;
+    //  ^^^^^^^ |i32| -> i32
+}
+
+fn fallible() -> ControlFlow<()> {
+    let strukt = Struct::try_new()?;
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn shows_constructor_type_hints_when_enabled() {
+        check_types(
+            r#"
+//- minicore: try
+use core::ops::ControlFlow;
+
+struct Struct;
+struct TupleStruct();
+
+impl Struct {
+    fn new() -> Self {
+        Struct
+    }
+    fn try_new() -> ControlFlow<(), Self> {
+        ControlFlow::Continue(Struct)
+    }
+}
+
+struct Generic<T>(T);
+impl Generic<i32> {
+    fn new() -> Self {
+        Generic(0)
+    }
+}
+
+fn main() {
+    let strukt = Struct::new();
+     // ^^^^^^ Struct
+    let tuple_struct = TupleStruct();
+     // ^^^^^^^^^^^^ TupleStruct
+    let generic0 = Generic::new();
+     // ^^^^^^^^ Generic<i32>
+    let generic1 = Generic::<i32>::new();
+     // ^^^^^^^^ Generic<i32>
+    let generic2 = <Generic<i32>>::new();
+     // ^^^^^^^^ Generic<i32>
+}
+
+fn fallible() -> ControlFlow<()> {
+    let strukt = Struct::try_new()?;
+     // ^^^^^^ Struct
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn closures() {
+        check(
+            r#"
+fn main() {
+    let mut start = 0;
+          //^^^^^ i32
+    (0..2).for_each(|increment      | { start += increment; });
+                   //^^^^^^^^^ i32
+
+    let multiply =
+      //^^^^^^^^ |i32, i32| -> i32
+      | a,     b| a * b
+      //^ i32  ^ i32
+
+    ;
+
+    let _: i32 = multiply(1,  2);
+                        //^ a ^ b
+    let multiply_ref = &multiply;
+      //^^^^^^^^^^^^ &|i32, i32| -> i32
+
+    let return_42 = || 42;
+      //^^^^^^^^^ || -> i32
+      || { 42 };
+    //^^ i32
+}"#,
+        );
+    }
+
+    #[test]
+    fn return_type_hints_for_closure_without_block() {
+        check_with_config(
+            InlayHintsConfig {
+                closure_return_type_hints: ClosureReturnTypeHints::Always,
+                ..DISABLED_CONFIG
+            },
+            r#"
+fn main() {
+    let a = || { 0 };
+          //^^ i32
+    let b = || 0;
+          //^^ i32
+}"#,
+        );
+    }
+
+    #[test]
+    fn skip_closure_type_hints() {
+        check_with_config(
+            InlayHintsConfig {
+                type_hints: true,
+                hide_closure_initialization_hints: true,
+                ..DISABLED_CONFIG
+            },
+            r#"
+//- minicore: fn
+fn main() {
+    let multiple_2 = |x: i32| { x * 2 };
+
+    let multiple_2 = |x: i32| x * 2;
+    //  ^^^^^^^^^^ |i32| -> i32
+
+    let (not) = (|x: bool| { !x });
+    //   ^^^ |bool| -> bool
+
+    let (is_zero, _b) = (|x: usize| { x == 0 }, false);
+    //   ^^^^^^^ |usize| -> bool
+    //            ^^ bool
+
+    let plus_one = |x| { x + 1 };
+    //              ^ u8
+    foo(plus_one);
+
+    let add_mul = bar(|x: u8| { x + 1 });
+    //  ^^^^^^^ impl FnOnce(u8) -> u8 + ?Sized
+
+    let closure = if let Some(6) = add_mul(2).checked_sub(1) {
+    //  ^^^^^^^ fn(i32) -> i32
+        |x: i32| { x * 2 }
+    } else {
+        |x: i32| { x * 3 }
+    };
+}
+
+fn foo(f: impl FnOnce(u8) -> u8) {}
+
+fn bar(f: impl FnOnce(u8) -> u8) -> impl FnOnce(u8) -> u8 {
+    move |x: u8| f(x) * 2
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn hint_truncation() {
+        check_with_config(
+            InlayHintsConfig { max_length: Some(8), ..TEST_CONFIG },
+            r#"
+struct Smol<T>(T);
+
+struct VeryLongOuterName<T>(T);
+
+fn main() {
+    let a = Smol(0u32);
+      //^ Smol<u32>
+    let b = VeryLongOuterName(0usize);
+      //^ VeryLongOuterName<…>
+    let c = Smol(Smol(0u32))
+      //^ Smol<Smol<…>>
+}"#,
+        );
+    }
+
+    // Chaining hint tests
+
+    #[test]
+    fn chaining_hints_ignore_comments() {
+        check_expect(
+            InlayHintsConfig { type_hints: false, chaining_hints: true, ..DISABLED_CONFIG },
+            r#"
+struct A(B);
+impl A { fn into_b(self) -> B { self.0 } }
+struct B(C);
+impl B { fn into_c(self) -> C { self.0 } }
+struct C;
+
+fn main() {
+    let c = A(B(C))
+        .into_b() // This is a comment
+        // This is another comment
+        .into_c();
+}
+"#,
+            expect![[r#"
+                [
+                    InlayHint {
+                        range: 147..172,
+                        kind: ChainingHint,
+                        label: [
+                            "B",
+                        ],
+                        tooltip: Some(
+                            HoverRanged(
+                                FileId(
+                                    0,
+                                ),
+                                147..172,
+                            ),
+                        ),
+                    },
+                    InlayHint {
+                        range: 147..154,
+                        kind: ChainingHint,
+                        label: [
+                            "A",
+                        ],
+                        tooltip: Some(
+                            HoverRanged(
+                                FileId(
+                                    0,
+                                ),
+                                147..154,
+                            ),
+                        ),
+                    },
+                ]
+            "#]],
+        );
+    }
+
+    #[test]
+    fn chaining_hints_without_newlines() {
+        check_chains(
+            r#"
+struct A(B);
+impl A { fn into_b(self) -> B { self.0 } }
+struct B(C);
+impl B { fn into_c(self) -> C { self.0 } }
+struct C;
+
+fn main() {
+    let c = A(B(C)).into_b().into_c();
+}"#,
+        );
+    }
+
+    #[test]
+    fn struct_access_chaining_hints() {
+        check_expect(
+            InlayHintsConfig { chaining_hints: true, ..DISABLED_CONFIG },
+            r#"
+struct A { pub b: B }
+struct B { pub c: C }
+struct C(pub bool);
+struct D;
+
+impl D {
+    fn foo(&self) -> i32 { 42 }
+}
+
+fn main() {
+    let x = A { b: B { c: C(true) } }
+        .b
+        .c
+        .0;
+    let x = D
+        .foo();
+}"#,
+            expect![[r#"
+                [
+                    InlayHint {
+                        range: 143..190,
+                        kind: ChainingHint,
+                        label: [
+                            "C",
+                        ],
+                        tooltip: Some(
+                            HoverRanged(
+                                FileId(
+                                    0,
+                                ),
+                                143..190,
+                            ),
+                        ),
+                    },
+                    InlayHint {
+                        range: 143..179,
+                        kind: ChainingHint,
+                        label: [
+                            "B",
+                        ],
+                        tooltip: Some(
+                            HoverRanged(
+                                FileId(
+                                    0,
+                                ),
+                                143..179,
+                            ),
+                        ),
+                    },
+                ]
+            "#]],
+        );
+    }
+
+    #[test]
+    fn generic_chaining_hints() {
+        check_expect(
+            InlayHintsConfig { chaining_hints: true, ..DISABLED_CONFIG },
+            r#"
+struct A<T>(T);
+struct B<T>(T);
+struct C<T>(T);
+struct X<T,R>(T, R);
+
+impl<T> A<T> {
+    fn new(t: T) -> Self { A(t) }
+    fn into_b(self) -> B<T> { B(self.0) }
+}
+impl<T> B<T> {
+    fn into_c(self) -> C<T> { C(self.0) }
+}
+fn main() {
+    let c = A::new(X(42, true))
+        .into_b()
+        .into_c();
+}
+"#,
+            expect![[r#"
+                [
+                    InlayHint {
+                        range: 246..283,
+                        kind: ChainingHint,
+                        label: [
+                            "B<X<i32, bool>>",
+                        ],
+                        tooltip: Some(
+                            HoverRanged(
+                                FileId(
+                                    0,
+                                ),
+                                246..283,
+                            ),
+                        ),
+                    },
+                    InlayHint {
+                        range: 246..265,
+                        kind: ChainingHint,
+                        label: [
+                            "A<X<i32, bool>>",
+                        ],
+                        tooltip: Some(
+                            HoverRanged(
+                                FileId(
+                                    0,
+                                ),
+                                246..265,
+                            ),
+                        ),
+                    },
+                ]
+            "#]],
+        );
+    }
+
+    #[test]
+    fn shorten_iterator_chaining_hints() {
+        check_expect(
+            InlayHintsConfig { chaining_hints: true, ..DISABLED_CONFIG },
+            r#"
+//- minicore: iterators
+use core::iter;
+
+struct MyIter;
+
+impl Iterator for MyIter {
+    type Item = ();
+    fn next(&mut self) -> Option<Self::Item> {
+        None
+    }
+}
+
+fn main() {
+    let _x = MyIter.by_ref()
+        .take(5)
+        .by_ref()
+        .take(5)
+        .by_ref();
+}
+"#,
+            expect![[r#"
+                [
+                    InlayHint {
+                        range: 174..241,
+                        kind: ChainingHint,
+                        label: [
+                            "impl Iterator<Item = ()>",
+                        ],
+                        tooltip: Some(
+                            HoverRanged(
+                                FileId(
+                                    0,
+                                ),
+                                174..241,
+                            ),
+                        ),
+                    },
+                    InlayHint {
+                        range: 174..224,
+                        kind: ChainingHint,
+                        label: [
+                            "impl Iterator<Item = ()>",
+                        ],
+                        tooltip: Some(
+                            HoverRanged(
+                                FileId(
+                                    0,
+                                ),
+                                174..224,
+                            ),
+                        ),
+                    },
+                    InlayHint {
+                        range: 174..206,
+                        kind: ChainingHint,
+                        label: [
+                            "impl Iterator<Item = ()>",
+                        ],
+                        tooltip: Some(
+                            HoverRanged(
+                                FileId(
+                                    0,
+                                ),
+                                174..206,
+                            ),
+                        ),
+                    },
+                    InlayHint {
+                        range: 174..189,
+                        kind: ChainingHint,
+                        label: [
+                            "&mut MyIter",
+                        ],
+                        tooltip: Some(
+                            HoverRanged(
+                                FileId(
+                                    0,
+                                ),
+                                174..189,
+                            ),
+                        ),
+                    },
+                ]
+            "#]],
+        );
+    }
+
+    #[test]
+    fn hints_in_attr_call() {
+        check_expect(
+            TEST_CONFIG,
+            r#"
+//- proc_macros: identity, input_replace
+struct Struct;
+impl Struct {
+    fn chain(self) -> Self {
+        self
+    }
+}
+#[proc_macros::identity]
+fn main() {
+    let strukt = Struct;
+    strukt
+        .chain()
+        .chain()
+        .chain();
+    Struct::chain(strukt);
+}
+"#,
+            expect![[r#"
+                [
+                    InlayHint {
+                        range: 124..130,
+                        kind: TypeHint,
+                        label: [
+                            "Struct",
+                        ],
+                        tooltip: Some(
+                            HoverRanged(
+                                FileId(
+                                    0,
+                                ),
+                                124..130,
+                            ),
+                        ),
+                    },
+                    InlayHint {
+                        range: 145..185,
+                        kind: ChainingHint,
+                        label: [
+                            "Struct",
+                        ],
+                        tooltip: Some(
+                            HoverRanged(
+                                FileId(
+                                    0,
+                                ),
+                                145..185,
+                            ),
+                        ),
+                    },
+                    InlayHint {
+                        range: 145..168,
+                        kind: ChainingHint,
+                        label: [
+                            "Struct",
+                        ],
+                        tooltip: Some(
+                            HoverRanged(
+                                FileId(
+                                    0,
+                                ),
+                                145..168,
+                            ),
+                        ),
+                    },
+                    InlayHint {
+                        range: 222..228,
+                        kind: ParameterHint,
+                        label: [
+                            "self",
+                        ],
+                        tooltip: Some(
+                            HoverOffset(
+                                FileId(
+                                    0,
+                                ),
+                                42,
+                            ),
+                        ),
+                    },
+                ]
+            "#]],
+        );
+    }
+
+    #[test]
+    fn hints_lifetimes() {
+        check(
+            r#"
+fn empty() {}
+
+fn no_gpl(a: &()) {}
+ //^^^^^^<'0>
+          // ^'0
+fn empty_gpl<>(a: &()) {}
+      //    ^'0   ^'0
+fn partial<'b>(a: &(), b: &'b ()) {}
+//        ^'0, $  ^'0
+fn partial<'a>(a: &'a (), b: &()) {}
+//        ^'0, $             ^'0
+
+fn single_ret(a: &()) -> &() {}
+// ^^^^^^^^^^<'0>
+              // ^'0     ^'0
+fn full_mul(a: &(), b: &()) {}
+// ^^^^^^^^<'0, '1>
+            // ^'0     ^'1
+
+fn foo<'c>(a: &'c ()) -> &() {}
+                      // ^'c
+
+fn nested_in(a: &   &X< &()>) {}
+// ^^^^^^^^^<'0, '1, '2>
+              //^'0 ^'1 ^'2
+fn nested_out(a: &()) -> &   &X< &()>{}
+// ^^^^^^^^^^<'0>
+               //^'0     ^'0 ^'0 ^'0
+
+impl () {
+    fn foo(&self) {}
+    // ^^^<'0>
+        // ^'0
+    fn foo(&self) -> &() {}
+    // ^^^<'0>
+        // ^'0       ^'0
+    fn foo(&self, a: &()) -> &() {}
+    // ^^^<'0, '1>
+        // ^'0       ^'1     ^'0
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn hints_lifetimes_named() {
+        check_with_config(
+            InlayHintsConfig { param_names_for_lifetime_elision_hints: true, ..TEST_CONFIG },
+            r#"
+fn nested_in<'named>(named: &        &X<      &()>) {}
+//          ^'named1, 'named2, 'named3, $
+                          //^'named1 ^'named2 ^'named3
+"#,
+        );
+    }
+
+    #[test]
+    fn hints_lifetimes_trivial_skip() {
+        check_with_config(
+            InlayHintsConfig {
+                lifetime_elision_hints: LifetimeElisionHints::SkipTrivial,
+                ..TEST_CONFIG
+            },
+            r#"
+fn no_gpl(a: &()) {}
+fn empty_gpl<>(a: &()) {}
+fn partial<'b>(a: &(), b: &'b ()) {}
+fn partial<'a>(a: &'a (), b: &()) {}
+
+fn single_ret(a: &()) -> &() {}
+// ^^^^^^^^^^<'0>
+              // ^'0     ^'0
+fn full_mul(a: &(), b: &()) {}
+
+fn foo<'c>(a: &'c ()) -> &() {}
+                      // ^'c
+
+fn nested_in(a: &   &X< &()>) {}
+fn nested_out(a: &()) -> &   &X< &()>{}
+// ^^^^^^^^^^<'0>
+               //^'0     ^'0 ^'0 ^'0
+
+impl () {
+    fn foo(&self) {}
+    fn foo(&self) -> &() {}
+    // ^^^<'0>
+        // ^'0       ^'0
+    fn foo(&self, a: &()) -> &() {}
+    // ^^^<'0, '1>
+        // ^'0       ^'1     ^'0
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn hints_lifetimes_static() {
+        check_with_config(
+            InlayHintsConfig {
+                lifetime_elision_hints: LifetimeElisionHints::Always,
+                ..TEST_CONFIG
+            },
+            r#"
+trait Trait {}
+static S: &str = "";
+//        ^'static
+const C: &str = "";
+//       ^'static
+const C: &dyn Trait = panic!();
+//       ^'static
+
+impl () {
+    const C: &str = "";
+    const C: &dyn Trait = panic!();
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn hints_binding_modes() {
+        check_with_config(
+            InlayHintsConfig { binding_mode_hints: true, ..DISABLED_CONFIG },
+            r#"
+fn __(
+    (x,): (u32,),
+    (x,): &(u32,),
+  //^^^^&
+   //^ ref
+    (x,): &mut (u32,)
+  //^^^^&mut
+   //^ ref mut
+) {
+    let (x,) = (0,);
+    let (x,) = &(0,);
+      //^^^^ &
+       //^ ref
+    let (x,) = &mut (0,);
+      //^^^^ &mut
+       //^ ref mut
+    let &mut (x,) = &mut (0,);
+    let (ref mut x,) = &mut (0,);
+      //^^^^^^^^^^^^ &mut
+    let &mut (ref mut x,) = &mut (0,);
+    let (mut x,) = &mut (0,);
+      //^^^^^^^^ &mut
+    match (0,) {
+        (x,) => ()
+    }
+    match &(0,) {
+        (x,) => ()
+      //^^^^ &
+       //^ ref
+    }
+    match &mut (0,) {
+        (x,) => ()
+      //^^^^ &mut
+       //^ ref mut
+    }
+}"#,
+        );
+    }
+
+    #[test]
+    fn hints_closing_brace() {
+        check_with_config(
+            InlayHintsConfig { closing_brace_hints_min_lines: Some(2), ..DISABLED_CONFIG },
+            r#"
+fn a() {}
+
+fn f() {
+} // no hint unless `}` is the last token on the line
+
+fn g() {
+  }
+//^ fn g
+
+fn h<T>(with: T, arguments: u8, ...) {
+  }
+//^ fn h
+
+trait Tr {
+    fn f();
+    fn g() {
+    }
+  //^ fn g
+  }
+//^ trait Tr
+impl Tr for () {
+  }
+//^ impl Tr for ()
+impl dyn Tr {
+  }
+//^ impl dyn Tr
+
+static S0: () = 0;
+static S1: () = {};
+static S2: () = {
+ };
+//^ static S2
+const _: () = {
+ };
+//^ const _
+
+mod m {
+  }
+//^ mod m
+
+m! {}
+m!();
+m!(
+ );
+//^ m!
+
+m! {
+  }
+//^ m!
+
+fn f() {
+    let v = vec![
+    ];
+  }
+//^ fn f
+"#,
+        );
+    }
+
+    #[test]
+    fn adjustment_hints() {
+        check_with_config(
+            InlayHintsConfig { adjustment_hints: AdjustmentHints::Always, ..DISABLED_CONFIG },
+            r#"
+//- minicore: coerce_unsized
+fn main() {
+    let _: u32         = loop {};
+                       //^^^^^^^<never-to-any>
+    let _: &u32        = &mut 0;
+                       //^^^^^^&
+                       //^^^^^^*
+    let _: &mut u32    = &mut 0;
+                       //^^^^^^&mut $
+                       //^^^^^^*
+    let _: *const u32  = &mut 0;
+                       //^^^^^^&raw const $
+                       //^^^^^^*
+    let _: *mut u32    = &mut 0;
+                       //^^^^^^&raw mut $
+                       //^^^^^^*
+    let _: fn()        = main;
+                       //^^^^<fn-item-to-fn-pointer>
+    let _: unsafe fn() = main;
+                       //^^^^<safe-fn-pointer-to-unsafe-fn-pointer>
+                       //^^^^<fn-item-to-fn-pointer>
+    let _: unsafe fn() = main as fn();
+                       //^^^^^^^^^^^^<safe-fn-pointer-to-unsafe-fn-pointer>
+    let _: fn()        = || {};
+                       //^^^^^<closure-to-fn-pointer>
+    let _: unsafe fn() = || {};
+                       //^^^^^<closure-to-unsafe-fn-pointer>
+    let _: *const u32  = &mut 0u32 as *mut u32;
+                       //^^^^^^^^^^^^^^^^^^^^^<mut-ptr-to-const-ptr>
+    let _: &mut [_]    = &mut [0; 0];
+                       //^^^^^^^^^^^<unsize>
+                       //^^^^^^^^^^^&mut $
+                       //^^^^^^^^^^^*
+
+    Struct.consume();
+    Struct.by_ref();
+  //^^^^^^(
+  //^^^^^^&
+  //^^^^^^)
+    Struct.by_ref_mut();
+  //^^^^^^(
+  //^^^^^^&mut $
+  //^^^^^^)
+
+    (&Struct).consume();
+   //^^^^^^^*
+    (&Struct).by_ref();
+
+    (&mut Struct).consume();
+   //^^^^^^^^^^^*
+    (&mut Struct).by_ref();
+   //^^^^^^^^^^^&
+   //^^^^^^^^^^^*
+    (&mut Struct).by_ref_mut();
+
+    // Check that block-like expressions don't duplicate hints
+    let _: &mut [u32] = (&mut []);
+                       //^^^^^^^<unsize>
+                       //^^^^^^^&mut $
+                       //^^^^^^^*
+    let _: &mut [u32] = { &mut [] };
+                        //^^^^^^^<unsize>
+                        //^^^^^^^&mut $
+                        //^^^^^^^*
+    let _: &mut [u32] = unsafe { &mut [] };
+                               //^^^^^^^<unsize>
+                               //^^^^^^^&mut $
+                               //^^^^^^^*
+    let _: &mut [u32] = if true {
+        &mut []
+      //^^^^^^^<unsize>
+      //^^^^^^^&mut $
+      //^^^^^^^*
+    } else {
+        loop {}
+      //^^^^^^^<never-to-any>
+    };
+    let _: &mut [u32] = match () { () => &mut [] }
+                                       //^^^^^^^<unsize>
+                                       //^^^^^^^&mut $
+                                       //^^^^^^^*
+}
+
+#[derive(Copy, Clone)]
+struct Struct;
+impl Struct {
+    fn consume(self) {}
+    fn by_ref(&self) {}
+    fn by_ref_mut(&mut self) {}
+}
+trait Trait {}
+impl Trait for Struct {}
+"#,
+        )
+    }
 }
diff --git a/crates/ide/src/inlay_hints/adjustment.rs b/crates/ide/src/inlay_hints/adjustment.rs
deleted file mode 100644
index 983aa1dfd5..0000000000
--- a/crates/ide/src/inlay_hints/adjustment.rs
+++ /dev/null
@@ -1,216 +0,0 @@
-//! Implementation of "adjustment" inlay hints:
-//! ```no_run
-//! let _: u32  = /* <never-to-any> */ loop {};
-//! let _: &u32 = /* &* */ &mut 0;
-//! ```
-use hir::{Adjust, AutoBorrow, Mutability, OverloadedDeref, PointerCast, Safety, Semantics};
-use ide_db::RootDatabase;
-
-use syntax::ast::{self, AstNode};
-
-use crate::{AdjustmentHints, InlayHint, InlayHintsConfig, InlayKind};
-
-pub(super) fn hints(
-    acc: &mut Vec<InlayHint>,
-    sema: &Semantics<'_, RootDatabase>,
-    config: &InlayHintsConfig,
-    expr: &ast::Expr,
-) -> Option<()> {
-    if config.adjustment_hints == AdjustmentHints::Never {
-        return None;
-    }
-
-    // These inherit from the inner expression which would result in duplicate hints
-    if let ast::Expr::ParenExpr(_)
-    | ast::Expr::IfExpr(_)
-    | ast::Expr::BlockExpr(_)
-    | ast::Expr::MatchExpr(_) = expr
-    {
-        return None;
-    }
-
-    let parent = expr.syntax().parent().and_then(ast::Expr::cast);
-    let descended = sema.descend_node_into_attributes(expr.clone()).pop();
-    let desc_expr = descended.as_ref().unwrap_or(expr);
-    let adjustments = sema.expr_adjustments(desc_expr).filter(|it| !it.is_empty())?;
-    let needs_parens = match parent {
-        Some(parent) => {
-            match parent {
-                ast::Expr::AwaitExpr(_)
-                | ast::Expr::CallExpr(_)
-                | ast::Expr::CastExpr(_)
-                | ast::Expr::FieldExpr(_)
-                | ast::Expr::MethodCallExpr(_)
-                | ast::Expr::TryExpr(_) => true,
-                // FIXME: shorthands need special casing, though not sure if adjustments are even valid there
-                ast::Expr::RecordExpr(_) => false,
-                ast::Expr::IndexExpr(index) => index.base().as_ref() == Some(expr),
-                _ => false,
-            }
-        }
-        None => false,
-    };
-    if needs_parens {
-        acc.push(InlayHint {
-            range: expr.syntax().text_range(),
-            kind: InlayKind::OpeningParenthesis,
-            label: "(".into(),
-            tooltip: None,
-        });
-    }
-    for adjustment in adjustments.into_iter().rev() {
-        // FIXME: Add some nicer tooltips to each of these
-        let text = match adjustment {
-            Adjust::NeverToAny if config.adjustment_hints == AdjustmentHints::Always => {
-                "<never-to-any>"
-            }
-            Adjust::Deref(None) => "*",
-            Adjust::Deref(Some(OverloadedDeref(Mutability::Mut))) => "*",
-            Adjust::Deref(Some(OverloadedDeref(Mutability::Shared))) => "*",
-            Adjust::Borrow(AutoBorrow::Ref(Mutability::Shared)) => "&",
-            Adjust::Borrow(AutoBorrow::Ref(Mutability::Mut)) => "&mut ",
-            Adjust::Borrow(AutoBorrow::RawPtr(Mutability::Shared)) => "&raw const ",
-            Adjust::Borrow(AutoBorrow::RawPtr(Mutability::Mut)) => "&raw mut ",
-            // some of these could be represented via `as` casts, but that's not too nice and
-            // handling everything as a prefix expr makes the `(` and `)` insertion easier
-            Adjust::Pointer(cast) if config.adjustment_hints == AdjustmentHints::Always => {
-                match cast {
-                    PointerCast::ReifyFnPointer => "<fn-item-to-fn-pointer>",
-                    PointerCast::UnsafeFnPointer => "<safe-fn-pointer-to-unsafe-fn-pointer>",
-                    PointerCast::ClosureFnPointer(Safety::Unsafe) => {
-                        "<closure-to-unsafe-fn-pointer>"
-                    }
-                    PointerCast::ClosureFnPointer(Safety::Safe) => "<closure-to-fn-pointer>",
-                    PointerCast::MutToConstPointer => "<mut-ptr-to-const-ptr>",
-                    PointerCast::ArrayToPointer => "<array-ptr-to-element-ptr>",
-                    PointerCast::Unsize => "<unsize>",
-                }
-            }
-            _ => continue,
-        };
-        acc.push(InlayHint {
-            range: expr.syntax().text_range(),
-            kind: InlayKind::AdjustmentHint,
-            label: text.into(),
-            tooltip: None,
-        });
-    }
-    if needs_parens {
-        acc.push(InlayHint {
-            range: expr.syntax().text_range(),
-            kind: InlayKind::ClosingParenthesis,
-            label: ")".into(),
-            tooltip: None,
-        });
-    }
-    Some(())
-}
-
-#[cfg(test)]
-mod tests {
-    use crate::{
-        inlay_hints::tests::{check_with_config, DISABLED_CONFIG},
-        AdjustmentHints, InlayHintsConfig,
-    };
-
-    #[test]
-    fn adjustment_hints() {
-        check_with_config(
-            InlayHintsConfig { adjustment_hints: AdjustmentHints::Always, ..DISABLED_CONFIG },
-            r#"
-//- minicore: coerce_unsized
-fn main() {
-    let _: u32         = loop {};
-                       //^^^^^^^<never-to-any>
-    let _: &u32        = &mut 0;
-                       //^^^^^^&
-                       //^^^^^^*
-    let _: &mut u32    = &mut 0;
-                       //^^^^^^&mut $
-                       //^^^^^^*
-    let _: *const u32  = &mut 0;
-                       //^^^^^^&raw const $
-                       //^^^^^^*
-    let _: *mut u32    = &mut 0;
-                       //^^^^^^&raw mut $
-                       //^^^^^^*
-    let _: fn()        = main;
-                       //^^^^<fn-item-to-fn-pointer>
-    let _: unsafe fn() = main;
-                       //^^^^<safe-fn-pointer-to-unsafe-fn-pointer>
-                       //^^^^<fn-item-to-fn-pointer>
-    let _: unsafe fn() = main as fn();
-                       //^^^^^^^^^^^^<safe-fn-pointer-to-unsafe-fn-pointer>
-    let _: fn()        = || {};
-                       //^^^^^<closure-to-fn-pointer>
-    let _: unsafe fn() = || {};
-                       //^^^^^<closure-to-unsafe-fn-pointer>
-    let _: *const u32  = &mut 0u32 as *mut u32;
-                       //^^^^^^^^^^^^^^^^^^^^^<mut-ptr-to-const-ptr>
-    let _: &mut [_]    = &mut [0; 0];
-                       //^^^^^^^^^^^<unsize>
-                       //^^^^^^^^^^^&mut $
-                       //^^^^^^^^^^^*
-
-    Struct.consume();
-    Struct.by_ref();
-  //^^^^^^(
-  //^^^^^^&
-  //^^^^^^)
-    Struct.by_ref_mut();
-  //^^^^^^(
-  //^^^^^^&mut $
-  //^^^^^^)
-
-    (&Struct).consume();
-   //^^^^^^^*
-    (&Struct).by_ref();
-
-    (&mut Struct).consume();
-   //^^^^^^^^^^^*
-    (&mut Struct).by_ref();
-   //^^^^^^^^^^^&
-   //^^^^^^^^^^^*
-    (&mut Struct).by_ref_mut();
-
-    // Check that block-like expressions don't duplicate hints
-    let _: &mut [u32] = (&mut []);
-                       //^^^^^^^<unsize>
-                       //^^^^^^^&mut $
-                       //^^^^^^^*
-    let _: &mut [u32] = { &mut [] };
-                        //^^^^^^^<unsize>
-                        //^^^^^^^&mut $
-                        //^^^^^^^*
-    let _: &mut [u32] = unsafe { &mut [] };
-                               //^^^^^^^<unsize>
-                               //^^^^^^^&mut $
-                               //^^^^^^^*
-    let _: &mut [u32] = if true {
-        &mut []
-      //^^^^^^^<unsize>
-      //^^^^^^^&mut $
-      //^^^^^^^*
-    } else {
-        loop {}
-      //^^^^^^^<never-to-any>
-    };
-    let _: &mut [u32] = match () { () => &mut [] }
-                                       //^^^^^^^<unsize>
-                                       //^^^^^^^&mut $
-                                       //^^^^^^^*
-}
-
-#[derive(Copy, Clone)]
-struct Struct;
-impl Struct {
-    fn consume(self) {}
-    fn by_ref(&self) {}
-    fn by_ref_mut(&mut self) {}
-}
-trait Trait {}
-impl Trait for Struct {}
-"#,
-        )
-    }
-}
diff --git a/crates/ide/src/inlay_hints/bind_pat.rs b/crates/ide/src/inlay_hints/bind_pat.rs
deleted file mode 100644
index 754df09df1..0000000000
--- a/crates/ide/src/inlay_hints/bind_pat.rs
+++ /dev/null
@@ -1,961 +0,0 @@
-//! Implementation of "type" inlay hints:
-//! ```no_run
-//! fn f(a: i32, b: i32) -> i32 { a + b }
-//! let _x /* i32 */= f(4, 4);
-//! ```
-use hir::{HirDisplay, Semantics, TypeInfo};
-use ide_db::{base_db::FileId, famous_defs::FamousDefs, RootDatabase};
-
-use itertools::Itertools;
-use syntax::{
-    ast::{self, AstNode, HasName},
-    match_ast,
-};
-
-use crate::{
-    inlay_hints::{closure_has_block_body, hint_iterator},
-    InlayHint, InlayHintsConfig, InlayKind, InlayTooltip,
-};
-
-pub(super) fn hints(
-    acc: &mut Vec<InlayHint>,
-    sema: &Semantics<'_, RootDatabase>,
-    config: &InlayHintsConfig,
-    file_id: FileId,
-    pat: &ast::IdentPat,
-) -> Option<()> {
-    if !config.type_hints {
-        return None;
-    }
-
-    let descended = sema.descend_node_into_attributes(pat.clone()).pop();
-    let desc_pat = descended.as_ref().unwrap_or(pat);
-    let ty = sema.type_of_pat(&desc_pat.clone().into())?.original;
-
-    if should_not_display_type_hint(sema, config, pat, &ty) {
-        return None;
-    }
-
-    let krate = sema.scope(desc_pat.syntax())?.krate();
-    let famous_defs = FamousDefs(sema, krate);
-    let label = hint_iterator(sema, &famous_defs, config, &ty);
-
-    let label = match label {
-        Some(label) => label,
-        None => {
-            let ty_name = ty.display_truncated(sema.db, config.max_length).to_string();
-            if config.hide_named_constructor_hints
-                && is_named_constructor(sema, pat, &ty_name).is_some()
-            {
-                return None;
-            }
-            ty_name
-        }
-    };
-
-    acc.push(InlayHint {
-        range: match pat.name() {
-            Some(name) => name.syntax().text_range(),
-            None => pat.syntax().text_range(),
-        },
-        kind: InlayKind::TypeHint,
-        label: label.into(),
-        tooltip: pat
-            .name()
-            .map(|it| it.syntax().text_range())
-            .map(|it| InlayTooltip::HoverRanged(file_id, it)),
-    });
-
-    Some(())
-}
-
-fn should_not_display_type_hint(
-    sema: &Semantics<'_, RootDatabase>,
-    config: &InlayHintsConfig,
-    bind_pat: &ast::IdentPat,
-    pat_ty: &hir::Type,
-) -> bool {
-    let db = sema.db;
-
-    if pat_ty.is_unknown() {
-        return true;
-    }
-
-    if let Some(hir::Adt::Struct(s)) = pat_ty.as_adt() {
-        if s.fields(db).is_empty() && s.name(db).to_smol_str() == bind_pat.to_string() {
-            return true;
-        }
-    }
-
-    if config.hide_closure_initialization_hints {
-        if let Some(parent) = bind_pat.syntax().parent() {
-            if let Some(it) = ast::LetStmt::cast(parent.clone()) {
-                if let Some(ast::Expr::ClosureExpr(closure)) = it.initializer() {
-                    if closure_has_block_body(&closure) {
-                        return true;
-                    }
-                }
-            }
-        }
-    }
-
-    for node in bind_pat.syntax().ancestors() {
-        match_ast! {
-            match node {
-                ast::LetStmt(it) => return it.ty().is_some(),
-                // FIXME: We might wanna show type hints in parameters for non-top level patterns as well
-                ast::Param(it) => return it.ty().is_some(),
-                ast::MatchArm(_) => return pat_is_enum_variant(db, bind_pat, pat_ty),
-                ast::LetExpr(_) => return pat_is_enum_variant(db, bind_pat, pat_ty),
-                ast::IfExpr(_) => return false,
-                ast::WhileExpr(_) => return false,
-                ast::ForExpr(it) => {
-                    // We *should* display hint only if user provided "in {expr}" and we know the type of expr (and it's not unit).
-                    // Type of expr should be iterable.
-                    return it.in_token().is_none() ||
-                        it.iterable()
-                            .and_then(|iterable_expr| sema.type_of_expr(&iterable_expr))
-                            .map(TypeInfo::original)
-                            .map_or(true, |iterable_ty| iterable_ty.is_unknown() || iterable_ty.is_unit())
-                },
-                _ => (),
-            }
-        }
-    }
-    false
-}
-
-fn is_named_constructor(
-    sema: &Semantics<'_, RootDatabase>,
-    pat: &ast::IdentPat,
-    ty_name: &str,
-) -> Option<()> {
-    let let_node = pat.syntax().parent()?;
-    let expr = match_ast! {
-        match let_node {
-            ast::LetStmt(it) => it.initializer(),
-            ast::LetExpr(it) => it.expr(),
-            _ => None,
-        }
-    }?;
-
-    let expr = sema.descend_node_into_attributes(expr.clone()).pop().unwrap_or(expr);
-    // unwrap postfix expressions
-    let expr = match expr {
-        ast::Expr::TryExpr(it) => it.expr(),
-        ast::Expr::AwaitExpr(it) => it.expr(),
-        expr => Some(expr),
-    }?;
-    let expr = match expr {
-        ast::Expr::CallExpr(call) => match call.expr()? {
-            ast::Expr::PathExpr(path) => path,
-            _ => return None,
-        },
-        ast::Expr::PathExpr(path) => path,
-        _ => return None,
-    };
-    let path = expr.path()?;
-
-    let callable = sema.type_of_expr(&ast::Expr::PathExpr(expr))?.original.as_callable(sema.db);
-    let callable_kind = callable.map(|it| it.kind());
-    let qual_seg = match callable_kind {
-        Some(hir::CallableKind::Function(_) | hir::CallableKind::TupleEnumVariant(_)) => {
-            path.qualifier()?.segment()
-        }
-        _ => path.segment(),
-    }?;
-
-    let ctor_name = match qual_seg.kind()? {
-        ast::PathSegmentKind::Name(name_ref) => {
-            match qual_seg.generic_arg_list().map(|it| it.generic_args()) {
-                Some(generics) => format!("{}<{}>", name_ref, generics.format(", ")),
-                None => name_ref.to_string(),
-            }
-        }
-        ast::PathSegmentKind::Type { type_ref: Some(ty), trait_ref: None } => ty.to_string(),
-        _ => return None,
-    };
-    (ctor_name == ty_name).then(|| ())
-}
-
-fn pat_is_enum_variant(db: &RootDatabase, bind_pat: &ast::IdentPat, pat_ty: &hir::Type) -> bool {
-    if let Some(hir::Adt::Enum(enum_data)) = pat_ty.as_adt() {
-        let pat_text = bind_pat.to_string();
-        enum_data
-            .variants(db)
-            .into_iter()
-            .map(|variant| variant.name(db).to_smol_str())
-            .any(|enum_name| enum_name == pat_text)
-    } else {
-        false
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    // This module also contains tests for super::closure_ret
-
-    use expect_test::expect;
-    use syntax::{TextRange, TextSize};
-    use test_utils::extract_annotations;
-
-    use crate::{fixture, inlay_hints::InlayHintsConfig};
-
-    use crate::inlay_hints::tests::{
-        check, check_expect, check_with_config, DISABLED_CONFIG, TEST_CONFIG,
-    };
-    use crate::ClosureReturnTypeHints;
-
-    #[track_caller]
-    fn check_types(ra_fixture: &str) {
-        check_with_config(InlayHintsConfig { type_hints: true, ..DISABLED_CONFIG }, ra_fixture);
-    }
-
-    #[test]
-    fn type_hints_only() {
-        check_types(
-            r#"
-fn foo(a: i32, b: i32) -> i32 { a + b }
-fn main() {
-    let _x = foo(4, 4);
-      //^^ i32
-}"#,
-        );
-    }
-
-    #[test]
-    fn type_hints_bindings_after_at() {
-        check_types(
-            r#"
-//- minicore: option
-fn main() {
-    let ref foo @ bar @ ref mut baz = 0;
-          //^^^ &i32
-                //^^^ i32
-                              //^^^ &mut i32
-    let [x @ ..] = [0];
-       //^ [i32; 1]
-    if let x @ Some(_) = Some(0) {}
-         //^ Option<i32>
-    let foo @ (bar, baz) = (3, 3);
-      //^^^ (i32, i32)
-             //^^^ i32
-                  //^^^ i32
-}"#,
-        );
-    }
-
-    #[test]
-    fn default_generic_types_should_not_be_displayed() {
-        check(
-            r#"
-struct Test<K, T = u8> { k: K, t: T }
-
-fn main() {
-    let zz = Test { t: 23u8, k: 33 };
-      //^^ Test<i32>
-    let zz_ref = &zz;
-      //^^^^^^ &Test<i32>
-    let test = || zz;
-      //^^^^ || -> Test<i32>
-}"#,
-        );
-    }
-
-    #[test]
-    fn shorten_iterators_in_associated_params() {
-        check_types(
-            r#"
-//- minicore: iterators
-use core::iter;
-
-pub struct SomeIter<T> {}
-
-impl<T> SomeIter<T> {
-    pub fn new() -> Self { SomeIter {} }
-    pub fn push(&mut self, t: T) {}
-}
-
-impl<T> Iterator for SomeIter<T> {
-    type Item = T;
-    fn next(&mut self) -> Option<Self::Item> {
-        None
-    }
-}
-
-fn main() {
-    let mut some_iter = SomeIter::new();
-          //^^^^^^^^^ SomeIter<Take<Repeat<i32>>>
-      some_iter.push(iter::repeat(2).take(2));
-    let iter_of_iters = some_iter.take(2);
-      //^^^^^^^^^^^^^ impl Iterator<Item = impl Iterator<Item = i32>>
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn iterator_hint_regression_issue_12674() {
-        // Ensure we don't crash while solving the projection type of iterators.
-        check_expect(
-            InlayHintsConfig { chaining_hints: true, ..DISABLED_CONFIG },
-            r#"
-//- minicore: iterators
-struct S<T>(T);
-impl<T> S<T> {
-    fn iter(&self) -> Iter<'_, T> { loop {} }
-}
-struct Iter<'a, T: 'a>(&'a T);
-impl<'a, T> Iterator for Iter<'a, T> {
-    type Item = &'a T;
-    fn next(&mut self) -> Option<Self::Item> { loop {} }
-}
-struct Container<'a> {
-    elements: S<&'a str>,
-}
-struct SliceIter<'a, T>(&'a T);
-impl<'a, T> Iterator for SliceIter<'a, T> {
-    type Item = &'a T;
-    fn next(&mut self) -> Option<Self::Item> { loop {} }
-}
-
-fn main(a: SliceIter<'_, Container>) {
-    a
-    .filter_map(|c| Some(c.elements.iter().filter_map(|v| Some(v))))
-    .map(|e| e);
-}
-            "#,
-            expect![[r#"
-                [
-                    InlayHint {
-                        range: 484..554,
-                        kind: ChainingHint,
-                        label: [
-                            "impl Iterator<Item = impl Iterator<Item = &&str>>",
-                        ],
-                        tooltip: Some(
-                            HoverRanged(
-                                FileId(
-                                    0,
-                                ),
-                                484..554,
-                            ),
-                        ),
-                    },
-                    InlayHint {
-                        range: 484..485,
-                        kind: ChainingHint,
-                        label: [
-                            "SliceIter<Container>",
-                        ],
-                        tooltip: Some(
-                            HoverRanged(
-                                FileId(
-                                    0,
-                                ),
-                                484..485,
-                            ),
-                        ),
-                    },
-                ]
-            "#]],
-        );
-    }
-
-    #[test]
-    fn infer_call_method_return_associated_types_with_generic() {
-        check_types(
-            r#"
-            pub trait Default {
-                fn default() -> Self;
-            }
-            pub trait Foo {
-                type Bar: Default;
-            }
-
-            pub fn quux<T: Foo>() -> T::Bar {
-                let y = Default::default();
-                  //^ <T as Foo>::Bar
-
-                y
-            }
-            "#,
-        );
-    }
-
-    #[test]
-    fn fn_hints() {
-        check_types(
-            r#"
-//- minicore: fn, sized
-fn foo() -> impl Fn() { loop {} }
-fn foo1() -> impl Fn(f64) { loop {} }
-fn foo2() -> impl Fn(f64, f64) { loop {} }
-fn foo3() -> impl Fn(f64, f64) -> u32 { loop {} }
-fn foo4() -> &'static dyn Fn(f64, f64) -> u32 { loop {} }
-fn foo5() -> &'static dyn Fn(&'static dyn Fn(f64, f64) -> u32, f64) -> u32 { loop {} }
-fn foo6() -> impl Fn(f64, f64) -> u32 + Sized { loop {} }
-fn foo7() -> *const (impl Fn(f64, f64) -> u32 + Sized) { loop {} }
-
-fn main() {
-    let foo = foo();
-     // ^^^ impl Fn()
-    let foo = foo1();
-     // ^^^ impl Fn(f64)
-    let foo = foo2();
-     // ^^^ impl Fn(f64, f64)
-    let foo = foo3();
-     // ^^^ impl Fn(f64, f64) -> u32
-    let foo = foo4();
-     // ^^^ &dyn Fn(f64, f64) -> u32
-    let foo = foo5();
-     // ^^^ &dyn Fn(&dyn Fn(f64, f64) -> u32, f64) -> u32
-    let foo = foo6();
-     // ^^^ impl Fn(f64, f64) -> u32
-    let foo = foo7();
-     // ^^^ *const impl Fn(f64, f64) -> u32
-}
-"#,
-        )
-    }
-
-    #[test]
-    fn check_hint_range_limit() {
-        let fixture = r#"
-        //- minicore: fn, sized
-        fn foo() -> impl Fn() { loop {} }
-        fn foo1() -> impl Fn(f64) { loop {} }
-        fn foo2() -> impl Fn(f64, f64) { loop {} }
-        fn foo3() -> impl Fn(f64, f64) -> u32 { loop {} }
-        fn foo4() -> &'static dyn Fn(f64, f64) -> u32 { loop {} }
-        fn foo5() -> &'static dyn Fn(&'static dyn Fn(f64, f64) -> u32, f64) -> u32 { loop {} }
-        fn foo6() -> impl Fn(f64, f64) -> u32 + Sized { loop {} }
-        fn foo7() -> *const (impl Fn(f64, f64) -> u32 + Sized) { loop {} }
-
-        fn main() {
-            let foo = foo();
-            let foo = foo1();
-            let foo = foo2();
-             // ^^^ impl Fn(f64, f64)
-            let foo = foo3();
-             // ^^^ impl Fn(f64, f64) -> u32
-            let foo = foo4();
-            let foo = foo5();
-            let foo = foo6();
-            let foo = foo7();
-        }
-        "#;
-        let (analysis, file_id) = fixture::file(fixture);
-        let expected = extract_annotations(&*analysis.file_text(file_id).unwrap());
-        let inlay_hints = analysis
-            .inlay_hints(
-                &InlayHintsConfig { type_hints: true, ..DISABLED_CONFIG },
-                file_id,
-                Some(TextRange::new(TextSize::from(500), TextSize::from(600))),
-            )
-            .unwrap();
-        let actual =
-            inlay_hints.into_iter().map(|it| (it.range, it.label.to_string())).collect::<Vec<_>>();
-        assert_eq!(expected, actual, "\nExpected:\n{:#?}\n\nActual:\n{:#?}", expected, actual);
-    }
-
-    #[test]
-    fn fn_hints_ptr_rpit_fn_parentheses() {
-        check_types(
-            r#"
-//- minicore: fn, sized
-trait Trait {}
-
-fn foo1() -> *const impl Fn() { loop {} }
-fn foo2() -> *const (impl Fn() + Sized) { loop {} }
-fn foo3() -> *const (impl Fn() + ?Sized) { loop {} }
-fn foo4() -> *const (impl Sized + Fn()) { loop {} }
-fn foo5() -> *const (impl ?Sized + Fn()) { loop {} }
-fn foo6() -> *const (impl Fn() + Trait) { loop {} }
-fn foo7() -> *const (impl Fn() + Sized + Trait) { loop {} }
-fn foo8() -> *const (impl Fn() + ?Sized + Trait) { loop {} }
-fn foo9() -> *const (impl Fn() -> u8 + ?Sized) { loop {} }
-fn foo10() -> *const (impl Fn() + Sized + ?Sized) { loop {} }
-
-fn main() {
-    let foo = foo1();
-    //  ^^^ *const impl Fn()
-    let foo = foo2();
-    //  ^^^ *const impl Fn()
-    let foo = foo3();
-    //  ^^^ *const (impl Fn() + ?Sized)
-    let foo = foo4();
-    //  ^^^ *const impl Fn()
-    let foo = foo5();
-    //  ^^^ *const (impl Fn() + ?Sized)
-    let foo = foo6();
-    //  ^^^ *const (impl Fn() + Trait)
-    let foo = foo7();
-    //  ^^^ *const (impl Fn() + Trait)
-    let foo = foo8();
-    //  ^^^ *const (impl Fn() + Trait + ?Sized)
-    let foo = foo9();
-    //  ^^^ *const (impl Fn() -> u8 + ?Sized)
-    let foo = foo10();
-    //  ^^^ *const impl Fn()
-}
-"#,
-        )
-    }
-
-    #[test]
-    fn unit_structs_have_no_type_hints() {
-        check_types(
-            r#"
-//- minicore: result
-struct SyntheticSyntax;
-
-fn main() {
-    match Ok(()) {
-        Ok(_) => (),
-        Err(SyntheticSyntax) => (),
-    }
-}"#,
-        );
-    }
-
-    #[test]
-    fn let_statement() {
-        check_types(
-            r#"
-#[derive(PartialEq)]
-enum Option<T> { None, Some(T) }
-
-#[derive(PartialEq)]
-struct Test { a: Option<u32>, b: u8 }
-
-fn main() {
-    struct InnerStruct {}
-
-    let test = 54;
-      //^^^^ i32
-    let test: i32 = 33;
-    let mut test = 33;
-          //^^^^ i32
-    let _ = 22;
-    let test = "test";
-      //^^^^ &str
-    let test = InnerStruct {};
-      //^^^^ InnerStruct
-
-    let test = unresolved();
-
-    let test = (42, 'a');
-      //^^^^ (i32, char)
-    let (a,    (b,     (c,)) = (2, (3, (9.2,));
-       //^ i32  ^ i32   ^ f64
-    let &x = &92;
-       //^ i32
-}"#,
-        );
-    }
-
-    #[test]
-    fn if_expr() {
-        check_types(
-            r#"
-//- minicore: option
-struct Test { a: Option<u32>, b: u8 }
-
-fn main() {
-    let test = Some(Test { a: Some(3), b: 1 });
-      //^^^^ Option<Test>
-    if let None = &test {};
-    if let test = &test {};
-         //^^^^ &Option<Test>
-    if let Some(test) = &test {};
-              //^^^^ &Test
-    if let Some(Test { a,             b }) = &test {};
-                     //^ &Option<u32> ^ &u8
-    if let Some(Test { a: x,             b: y }) = &test {};
-                        //^ &Option<u32>    ^ &u8
-    if let Some(Test { a: Some(x),  b: y }) = &test {};
-                             //^ &u32  ^ &u8
-    if let Some(Test { a: None,  b: y }) = &test {};
-                                  //^ &u8
-    if let Some(Test { b: y, .. }) = &test {};
-                        //^ &u8
-    if test == None {}
-}"#,
-        );
-    }
-
-    #[test]
-    fn while_expr() {
-        check_types(
-            r#"
-//- minicore: option
-struct Test { a: Option<u32>, b: u8 }
-
-fn main() {
-    let test = Some(Test { a: Some(3), b: 1 });
-      //^^^^ Option<Test>
-    while let Some(Test { a: Some(x),  b: y }) = &test {};
-                                //^ &u32  ^ &u8
-}"#,
-        );
-    }
-
-    #[test]
-    fn match_arm_list() {
-        check_types(
-            r#"
-//- minicore: option
-struct Test { a: Option<u32>, b: u8 }
-
-fn main() {
-    match Some(Test { a: Some(3), b: 1 }) {
-        None => (),
-        test => (),
-      //^^^^ Option<Test>
-        Some(Test { a: Some(x), b: y }) => (),
-                          //^ u32  ^ u8
-        _ => {}
-    }
-}"#,
-        );
-    }
-
-    #[test]
-    fn complete_for_hint() {
-        check_types(
-            r#"
-//- minicore: iterator
-pub struct Vec<T> {}
-
-impl<T> Vec<T> {
-    pub fn new() -> Self { Vec {} }
-    pub fn push(&mut self, t: T) {}
-}
-
-impl<T> IntoIterator for Vec<T> {
-    type Item = T;
-    type IntoIter = IntoIter<T>;
-}
-
-struct IntoIter<T> {}
-
-impl<T> Iterator for IntoIter<T> {
-    type Item = T;
-}
-
-fn main() {
-    let mut data = Vec::new();
-          //^^^^ Vec<&str>
-    data.push("foo");
-    for i in data {
-      //^ &str
-      let z = i;
-        //^ &str
-    }
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn multi_dyn_trait_bounds() {
-        check_types(
-            r#"
-pub struct Vec<T> {}
-
-impl<T> Vec<T> {
-    pub fn new() -> Self { Vec {} }
-}
-
-pub struct Box<T> {}
-
-trait Display {}
-auto trait Sync {}
-
-fn main() {
-    // The block expression wrapping disables the constructor hint hiding logic
-    let _v = { Vec::<Box<&(dyn Display + Sync)>>::new() };
-      //^^ Vec<Box<&(dyn Display + Sync)>>
-    let _v = { Vec::<Box<*const (dyn Display + Sync)>>::new() };
-      //^^ Vec<Box<*const (dyn Display + Sync)>>
-    let _v = { Vec::<Box<dyn Display + Sync>>::new() };
-      //^^ Vec<Box<dyn Display + Sync>>
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn shorten_iterator_hints() {
-        check_types(
-            r#"
-//- minicore: iterators
-use core::iter;
-
-struct MyIter;
-
-impl Iterator for MyIter {
-    type Item = ();
-    fn next(&mut self) -> Option<Self::Item> {
-        None
-    }
-}
-
-fn main() {
-    let _x = MyIter;
-      //^^ MyIter
-    let _x = iter::repeat(0);
-      //^^ impl Iterator<Item = i32>
-    fn generic<T: Clone>(t: T) {
-        let _x = iter::repeat(t);
-          //^^ impl Iterator<Item = T>
-        let _chained = iter::repeat(t).take(10);
-          //^^^^^^^^ impl Iterator<Item = T>
-    }
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn skip_constructor_and_enum_type_hints() {
-        check_with_config(
-            InlayHintsConfig {
-                type_hints: true,
-                hide_named_constructor_hints: true,
-                ..DISABLED_CONFIG
-            },
-            r#"
-//- minicore: try, option
-use core::ops::ControlFlow;
-
-mod x {
-    pub mod y { pub struct Foo; }
-    pub struct Foo;
-    pub enum AnotherEnum {
-        Variant()
-    };
-}
-struct Struct;
-struct TupleStruct();
-
-impl Struct {
-    fn new() -> Self {
-        Struct
-    }
-    fn try_new() -> ControlFlow<(), Self> {
-        ControlFlow::Continue(Struct)
-    }
-}
-
-struct Generic<T>(T);
-impl Generic<i32> {
-    fn new() -> Self {
-        Generic(0)
-    }
-}
-
-enum Enum {
-    Variant(u32)
-}
-
-fn times2(value: i32) -> i32 {
-    2 * value
-}
-
-fn main() {
-    let enumb = Enum::Variant(0);
-
-    let strukt = x::Foo;
-    let strukt = x::y::Foo;
-    let strukt = Struct;
-    let strukt = Struct::new();
-
-    let tuple_struct = TupleStruct();
-
-    let generic0 = Generic::new();
-    //  ^^^^^^^^ Generic<i32>
-    let generic1 = Generic(0);
-    //  ^^^^^^^^ Generic<i32>
-    let generic2 = Generic::<i32>::new();
-    let generic3 = <Generic<i32>>::new();
-    let generic4 = Generic::<i32>(0);
-
-
-    let option = Some(0);
-    //  ^^^^^^ Option<i32>
-    let func = times2;
-    //  ^^^^ fn times2(i32) -> i32
-    let closure = |x: i32| x * 2;
-    //  ^^^^^^^ |i32| -> i32
-}
-
-fn fallible() -> ControlFlow<()> {
-    let strukt = Struct::try_new()?;
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn shows_constructor_type_hints_when_enabled() {
-        check_types(
-            r#"
-//- minicore: try
-use core::ops::ControlFlow;
-
-struct Struct;
-struct TupleStruct();
-
-impl Struct {
-    fn new() -> Self {
-        Struct
-    }
-    fn try_new() -> ControlFlow<(), Self> {
-        ControlFlow::Continue(Struct)
-    }
-}
-
-struct Generic<T>(T);
-impl Generic<i32> {
-    fn new() -> Self {
-        Generic(0)
-    }
-}
-
-fn main() {
-    let strukt = Struct::new();
-     // ^^^^^^ Struct
-    let tuple_struct = TupleStruct();
-     // ^^^^^^^^^^^^ TupleStruct
-    let generic0 = Generic::new();
-     // ^^^^^^^^ Generic<i32>
-    let generic1 = Generic::<i32>::new();
-     // ^^^^^^^^ Generic<i32>
-    let generic2 = <Generic<i32>>::new();
-     // ^^^^^^^^ Generic<i32>
-}
-
-fn fallible() -> ControlFlow<()> {
-    let strukt = Struct::try_new()?;
-     // ^^^^^^ Struct
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn closures() {
-        check(
-            r#"
-fn main() {
-    let mut start = 0;
-          //^^^^^ i32
-    (0..2).for_each(|increment      | { start += increment; });
-                   //^^^^^^^^^ i32
-
-    let multiply =
-      //^^^^^^^^ |i32, i32| -> i32
-      | a,     b| a * b
-      //^ i32  ^ i32
-
-    ;
-
-    let _: i32 = multiply(1,  2);
-                        //^ a ^ b
-    let multiply_ref = &multiply;
-      //^^^^^^^^^^^^ &|i32, i32| -> i32
-
-    let return_42 = || 42;
-      //^^^^^^^^^ || -> i32
-      || { 42 };
-    //^^ i32
-}"#,
-        );
-    }
-
-    #[test]
-    fn return_type_hints_for_closure_without_block() {
-        check_with_config(
-            InlayHintsConfig {
-                closure_return_type_hints: ClosureReturnTypeHints::Always,
-                ..DISABLED_CONFIG
-            },
-            r#"
-fn main() {
-    let a = || { 0 };
-          //^^ i32
-    let b = || 0;
-          //^^ i32
-}"#,
-        );
-    }
-
-    #[test]
-    fn skip_closure_type_hints() {
-        check_with_config(
-            InlayHintsConfig {
-                type_hints: true,
-                hide_closure_initialization_hints: true,
-                ..DISABLED_CONFIG
-            },
-            r#"
-//- minicore: fn
-fn main() {
-    let multiple_2 = |x: i32| { x * 2 };
-
-    let multiple_2 = |x: i32| x * 2;
-    //  ^^^^^^^^^^ |i32| -> i32
-
-    let (not) = (|x: bool| { !x });
-    //   ^^^ |bool| -> bool
-
-    let (is_zero, _b) = (|x: usize| { x == 0 }, false);
-    //   ^^^^^^^ |usize| -> bool
-    //            ^^ bool
-
-    let plus_one = |x| { x + 1 };
-    //              ^ u8
-    foo(plus_one);
-
-    let add_mul = bar(|x: u8| { x + 1 });
-    //  ^^^^^^^ impl FnOnce(u8) -> u8 + ?Sized
-
-    let closure = if let Some(6) = add_mul(2).checked_sub(1) {
-    //  ^^^^^^^ fn(i32) -> i32
-        |x: i32| { x * 2 }
-    } else {
-        |x: i32| { x * 3 }
-    };
-}
-
-fn foo(f: impl FnOnce(u8) -> u8) {}
-
-fn bar(f: impl FnOnce(u8) -> u8) -> impl FnOnce(u8) -> u8 {
-    move |x: u8| f(x) * 2
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn hint_truncation() {
-        check_with_config(
-            InlayHintsConfig { max_length: Some(8), ..TEST_CONFIG },
-            r#"
-struct Smol<T>(T);
-
-struct VeryLongOuterName<T>(T);
-
-fn main() {
-    let a = Smol(0u32);
-      //^ Smol<u32>
-    let b = VeryLongOuterName(0usize);
-      //^ VeryLongOuterName<…>
-    let c = Smol(Smol(0u32))
-      //^ Smol<Smol<…>>
-}"#,
-        );
-    }
-}
diff --git a/crates/ide/src/inlay_hints/binding_mode.rs b/crates/ide/src/inlay_hints/binding_mode.rs
deleted file mode 100644
index 846f0a4e0c..0000000000
--- a/crates/ide/src/inlay_hints/binding_mode.rs
+++ /dev/null
@@ -1,141 +0,0 @@
-//! Implementation of "binding mode" inlay hints:
-//! ```no_run
-//! let /* & */ (/* ref */ x,) = &(0,);
-//! ```
-use hir::{Mutability, Semantics};
-use ide_db::RootDatabase;
-
-use syntax::ast::{self, AstNode};
-
-use crate::{InlayHint, InlayHintsConfig, InlayKind, InlayTooltip};
-
-pub(super) fn hints(
-    acc: &mut Vec<InlayHint>,
-    sema: &Semantics<'_, RootDatabase>,
-    config: &InlayHintsConfig,
-    pat: &ast::Pat,
-) -> Option<()> {
-    if !config.binding_mode_hints {
-        return None;
-    }
-
-    let outer_paren_pat = pat
-        .syntax()
-        .ancestors()
-        .skip(1)
-        .map_while(ast::Pat::cast)
-        .map_while(|pat| match pat {
-            ast::Pat::ParenPat(pat) => Some(pat),
-            _ => None,
-        })
-        .last();
-    let range =
-        outer_paren_pat.as_ref().map_or_else(|| pat.syntax(), |it| it.syntax()).text_range();
-    sema.pattern_adjustments(&pat).iter().for_each(|ty| {
-        let reference = ty.is_reference();
-        let mut_reference = ty.is_mutable_reference();
-        let r = match (reference, mut_reference) {
-            (true, true) => "&mut",
-            (true, false) => "&",
-            _ => return,
-        };
-        acc.push(InlayHint {
-            range,
-            kind: InlayKind::BindingModeHint,
-            label: r.to_string().into(),
-            tooltip: Some(InlayTooltip::String("Inferred binding mode".into())),
-        });
-    });
-    match pat {
-        ast::Pat::IdentPat(pat) if pat.ref_token().is_none() && pat.mut_token().is_none() => {
-            let bm = sema.binding_mode_of_pat(pat)?;
-            let bm = match bm {
-                hir::BindingMode::Move => return None,
-                hir::BindingMode::Ref(Mutability::Mut) => "ref mut",
-                hir::BindingMode::Ref(Mutability::Shared) => "ref",
-            };
-            acc.push(InlayHint {
-                range: pat.syntax().text_range(),
-                kind: InlayKind::BindingModeHint,
-                label: bm.to_string().into(),
-                tooltip: Some(InlayTooltip::String("Inferred binding mode".into())),
-            });
-        }
-        ast::Pat::OrPat(pat) if outer_paren_pat.is_none() => {
-            acc.push(InlayHint {
-                range: pat.syntax().text_range(),
-                kind: InlayKind::OpeningParenthesis,
-                label: "(".into(),
-                tooltip: None,
-            });
-            acc.push(InlayHint {
-                range: pat.syntax().text_range(),
-                kind: InlayKind::ClosingParenthesis,
-                label: ")".into(),
-                tooltip: None,
-            });
-        }
-        _ => (),
-    }
-
-    Some(())
-}
-
-#[cfg(test)]
-mod tests {
-    use crate::{
-        inlay_hints::tests::{check_with_config, DISABLED_CONFIG},
-        InlayHintsConfig,
-    };
-
-    #[test]
-    fn hints_binding_modes() {
-        check_with_config(
-            InlayHintsConfig { binding_mode_hints: true, ..DISABLED_CONFIG },
-            r#"
-fn __(
-    (x,): (u32,),
-    (x,): &(u32,),
-  //^^^^&
-   //^ ref
-    (x,): &mut (u32,)
-  //^^^^&mut
-   //^ ref mut
-) {
-    let (x,) = (0,);
-    let (x,) = &(0,);
-      //^^^^ &
-       //^ ref
-    let (x,) = &mut (0,);
-      //^^^^ &mut
-       //^ ref mut
-    let &mut (x,) = &mut (0,);
-    let (ref mut x,) = &mut (0,);
-      //^^^^^^^^^^^^ &mut
-    let &mut (ref mut x,) = &mut (0,);
-    let (mut x,) = &mut (0,);
-      //^^^^^^^^ &mut
-    match (0,) {
-        (x,) => ()
-    }
-    match &(0,) {
-        (x,) | (x,) => (),
-      //^^^^^^^^^^^&
-       //^ ref
-              //^ ref
-      //^^^^^^^^^^^(
-      //^^^^^^^^^^^)
-        ((x,) | (x,)) => (),
-      //^^^^^^^^^^^^^&
-        //^ ref
-               //^ ref
-    }
-    match &mut (0,) {
-        (x,) => ()
-      //^^^^ &mut
-       //^ ref mut
-    }
-}"#,
-        );
-    }
-}
diff --git a/crates/ide/src/inlay_hints/chaining.rs b/crates/ide/src/inlay_hints/chaining.rs
deleted file mode 100644
index 32421afd39..0000000000
--- a/crates/ide/src/inlay_hints/chaining.rs
+++ /dev/null
@@ -1,459 +0,0 @@
-//! Implementation of "chaining" inlay hints.
-use hir::{HirDisplay, Semantics};
-use ide_db::{famous_defs::FamousDefs, RootDatabase};
-use syntax::{
-    ast::{self, AstNode},
-    Direction, NodeOrToken, SyntaxKind, T,
-};
-
-use crate::{
-    inlay_hints::hint_iterator, FileId, InlayHint, InlayHintsConfig, InlayKind, InlayTooltip,
-};
-
-pub(super) fn hints(
-    acc: &mut Vec<InlayHint>,
-    sema: &Semantics<'_, RootDatabase>,
-    famous_defs: &FamousDefs<'_, '_>,
-    config: &InlayHintsConfig,
-    file_id: FileId,
-    expr: &ast::Expr,
-) -> Option<()> {
-    if !config.chaining_hints {
-        return None;
-    }
-
-    if matches!(expr, ast::Expr::RecordExpr(_)) {
-        return None;
-    }
-
-    let descended = sema.descend_node_into_attributes(expr.clone()).pop();
-    let desc_expr = descended.as_ref().unwrap_or(expr);
-
-    let mut tokens = expr
-        .syntax()
-        .siblings_with_tokens(Direction::Next)
-        .filter_map(NodeOrToken::into_token)
-        .filter(|t| match t.kind() {
-            SyntaxKind::WHITESPACE if !t.text().contains('\n') => false,
-            SyntaxKind::COMMENT => false,
-            _ => true,
-        });
-
-    // Chaining can be defined as an expression whose next sibling tokens are newline and dot
-    // Ignoring extra whitespace and comments
-    let next = tokens.next()?.kind();
-    if next == SyntaxKind::WHITESPACE {
-        let mut next_next = tokens.next()?.kind();
-        while next_next == SyntaxKind::WHITESPACE {
-            next_next = tokens.next()?.kind();
-        }
-        if next_next == T![.] {
-            let ty = sema.type_of_expr(desc_expr)?.original;
-            if ty.is_unknown() {
-                return None;
-            }
-            if matches!(expr, ast::Expr::PathExpr(_)) {
-                if let Some(hir::Adt::Struct(st)) = ty.as_adt() {
-                    if st.fields(sema.db).is_empty() {
-                        return None;
-                    }
-                }
-            }
-            acc.push(InlayHint {
-                range: expr.syntax().text_range(),
-                kind: InlayKind::ChainingHint,
-                label: hint_iterator(sema, &famous_defs, config, &ty)
-                    .unwrap_or_else(|| ty.display_truncated(sema.db, config.max_length).to_string())
-                    .into(),
-                tooltip: Some(InlayTooltip::HoverRanged(file_id, expr.syntax().text_range())),
-            });
-        }
-    }
-    Some(())
-}
-
-#[cfg(test)]
-mod tests {
-    use expect_test::expect;
-
-    use crate::{
-        inlay_hints::tests::{check_expect, check_with_config, DISABLED_CONFIG, TEST_CONFIG},
-        InlayHintsConfig,
-    };
-
-    #[track_caller]
-    fn check_chains(ra_fixture: &str) {
-        check_with_config(InlayHintsConfig { chaining_hints: true, ..DISABLED_CONFIG }, ra_fixture);
-    }
-
-    #[test]
-    fn chaining_hints_ignore_comments() {
-        check_expect(
-            InlayHintsConfig { type_hints: false, chaining_hints: true, ..DISABLED_CONFIG },
-            r#"
-struct A(B);
-impl A { fn into_b(self) -> B { self.0 } }
-struct B(C);
-impl B { fn into_c(self) -> C { self.0 } }
-struct C;
-
-fn main() {
-    let c = A(B(C))
-        .into_b() // This is a comment
-        // This is another comment
-        .into_c();
-}
-"#,
-            expect![[r#"
-                [
-                    InlayHint {
-                        range: 147..172,
-                        kind: ChainingHint,
-                        label: [
-                            "B",
-                        ],
-                        tooltip: Some(
-                            HoverRanged(
-                                FileId(
-                                    0,
-                                ),
-                                147..172,
-                            ),
-                        ),
-                    },
-                    InlayHint {
-                        range: 147..154,
-                        kind: ChainingHint,
-                        label: [
-                            "A",
-                        ],
-                        tooltip: Some(
-                            HoverRanged(
-                                FileId(
-                                    0,
-                                ),
-                                147..154,
-                            ),
-                        ),
-                    },
-                ]
-            "#]],
-        );
-    }
-
-    #[test]
-    fn chaining_hints_without_newlines() {
-        check_chains(
-            r#"
-struct A(B);
-impl A { fn into_b(self) -> B { self.0 } }
-struct B(C);
-impl B { fn into_c(self) -> C { self.0 } }
-struct C;
-
-fn main() {
-    let c = A(B(C)).into_b().into_c();
-}"#,
-        );
-    }
-
-    #[test]
-    fn struct_access_chaining_hints() {
-        check_expect(
-            InlayHintsConfig { chaining_hints: true, ..DISABLED_CONFIG },
-            r#"
-struct A { pub b: B }
-struct B { pub c: C }
-struct C(pub bool);
-struct D;
-
-impl D {
-    fn foo(&self) -> i32 { 42 }
-}
-
-fn main() {
-    let x = A { b: B { c: C(true) } }
-        .b
-        .c
-        .0;
-    let x = D
-        .foo();
-}"#,
-            expect![[r#"
-                [
-                    InlayHint {
-                        range: 143..190,
-                        kind: ChainingHint,
-                        label: [
-                            "C",
-                        ],
-                        tooltip: Some(
-                            HoverRanged(
-                                FileId(
-                                    0,
-                                ),
-                                143..190,
-                            ),
-                        ),
-                    },
-                    InlayHint {
-                        range: 143..179,
-                        kind: ChainingHint,
-                        label: [
-                            "B",
-                        ],
-                        tooltip: Some(
-                            HoverRanged(
-                                FileId(
-                                    0,
-                                ),
-                                143..179,
-                            ),
-                        ),
-                    },
-                ]
-            "#]],
-        );
-    }
-
-    #[test]
-    fn generic_chaining_hints() {
-        check_expect(
-            InlayHintsConfig { chaining_hints: true, ..DISABLED_CONFIG },
-            r#"
-struct A<T>(T);
-struct B<T>(T);
-struct C<T>(T);
-struct X<T,R>(T, R);
-
-impl<T> A<T> {
-    fn new(t: T) -> Self { A(t) }
-    fn into_b(self) -> B<T> { B(self.0) }
-}
-impl<T> B<T> {
-    fn into_c(self) -> C<T> { C(self.0) }
-}
-fn main() {
-    let c = A::new(X(42, true))
-        .into_b()
-        .into_c();
-}
-"#,
-            expect![[r#"
-                [
-                    InlayHint {
-                        range: 246..283,
-                        kind: ChainingHint,
-                        label: [
-                            "B<X<i32, bool>>",
-                        ],
-                        tooltip: Some(
-                            HoverRanged(
-                                FileId(
-                                    0,
-                                ),
-                                246..283,
-                            ),
-                        ),
-                    },
-                    InlayHint {
-                        range: 246..265,
-                        kind: ChainingHint,
-                        label: [
-                            "A<X<i32, bool>>",
-                        ],
-                        tooltip: Some(
-                            HoverRanged(
-                                FileId(
-                                    0,
-                                ),
-                                246..265,
-                            ),
-                        ),
-                    },
-                ]
-            "#]],
-        );
-    }
-
-    #[test]
-    fn shorten_iterator_chaining_hints() {
-        check_expect(
-            InlayHintsConfig { chaining_hints: true, ..DISABLED_CONFIG },
-            r#"
-//- minicore: iterators
-use core::iter;
-
-struct MyIter;
-
-impl Iterator for MyIter {
-    type Item = ();
-    fn next(&mut self) -> Option<Self::Item> {
-        None
-    }
-}
-
-fn main() {
-    let _x = MyIter.by_ref()
-        .take(5)
-        .by_ref()
-        .take(5)
-        .by_ref();
-}
-"#,
-            expect![[r#"
-                [
-                    InlayHint {
-                        range: 174..241,
-                        kind: ChainingHint,
-                        label: [
-                            "impl Iterator<Item = ()>",
-                        ],
-                        tooltip: Some(
-                            HoverRanged(
-                                FileId(
-                                    0,
-                                ),
-                                174..241,
-                            ),
-                        ),
-                    },
-                    InlayHint {
-                        range: 174..224,
-                        kind: ChainingHint,
-                        label: [
-                            "impl Iterator<Item = ()>",
-                        ],
-                        tooltip: Some(
-                            HoverRanged(
-                                FileId(
-                                    0,
-                                ),
-                                174..224,
-                            ),
-                        ),
-                    },
-                    InlayHint {
-                        range: 174..206,
-                        kind: ChainingHint,
-                        label: [
-                            "impl Iterator<Item = ()>",
-                        ],
-                        tooltip: Some(
-                            HoverRanged(
-                                FileId(
-                                    0,
-                                ),
-                                174..206,
-                            ),
-                        ),
-                    },
-                    InlayHint {
-                        range: 174..189,
-                        kind: ChainingHint,
-                        label: [
-                            "&mut MyIter",
-                        ],
-                        tooltip: Some(
-                            HoverRanged(
-                                FileId(
-                                    0,
-                                ),
-                                174..189,
-                            ),
-                        ),
-                    },
-                ]
-            "#]],
-        );
-    }
-
-    #[test]
-    fn hints_in_attr_call() {
-        check_expect(
-            TEST_CONFIG,
-            r#"
-//- proc_macros: identity, input_replace
-struct Struct;
-impl Struct {
-    fn chain(self) -> Self {
-        self
-    }
-}
-#[proc_macros::identity]
-fn main() {
-    let strukt = Struct;
-    strukt
-        .chain()
-        .chain()
-        .chain();
-    Struct::chain(strukt);
-}
-"#,
-            expect![[r#"
-                [
-                    InlayHint {
-                        range: 124..130,
-                        kind: TypeHint,
-                        label: [
-                            "Struct",
-                        ],
-                        tooltip: Some(
-                            HoverRanged(
-                                FileId(
-                                    0,
-                                ),
-                                124..130,
-                            ),
-                        ),
-                    },
-                    InlayHint {
-                        range: 145..185,
-                        kind: ChainingHint,
-                        label: [
-                            "Struct",
-                        ],
-                        tooltip: Some(
-                            HoverRanged(
-                                FileId(
-                                    0,
-                                ),
-                                145..185,
-                            ),
-                        ),
-                    },
-                    InlayHint {
-                        range: 145..168,
-                        kind: ChainingHint,
-                        label: [
-                            "Struct",
-                        ],
-                        tooltip: Some(
-                            HoverRanged(
-                                FileId(
-                                    0,
-                                ),
-                                145..168,
-                            ),
-                        ),
-                    },
-                    InlayHint {
-                        range: 222..228,
-                        kind: ParameterHint,
-                        label: [
-                            "self",
-                        ],
-                        tooltip: Some(
-                            HoverOffset(
-                                FileId(
-                                    0,
-                                ),
-                                42,
-                            ),
-                        ),
-                    },
-                ]
-            "#]],
-        );
-    }
-}
diff --git a/crates/ide/src/inlay_hints/closing_brace.rs b/crates/ide/src/inlay_hints/closing_brace.rs
deleted file mode 100644
index 57605b392a..0000000000
--- a/crates/ide/src/inlay_hints/closing_brace.rs
+++ /dev/null
@@ -1,193 +0,0 @@
-//! Implementation of "closing brace" inlay hints:
-//! ```no_run
-//! fn g() {
-//! } /* fn g */
-//! ```
-use hir::{HirDisplay, Semantics};
-use ide_db::{base_db::FileRange, RootDatabase};
-use syntax::{
-    ast::{self, AstNode, HasName},
-    match_ast, SyntaxKind, SyntaxNode, T,
-};
-
-use crate::{
-    inlay_hints::InlayHintLabelPart, FileId, InlayHint, InlayHintLabel, InlayHintsConfig, InlayKind,
-};
-
-pub(super) fn hints(
-    acc: &mut Vec<InlayHint>,
-    sema: &Semantics<'_, RootDatabase>,
-    config: &InlayHintsConfig,
-    file_id: FileId,
-    node: SyntaxNode,
-) -> Option<()> {
-    let min_lines = config.closing_brace_hints_min_lines?;
-
-    let name = |it: ast::Name| it.syntax().text_range();
-
-    let mut closing_token;
-    let (label, name_range) = if let Some(item_list) = ast::AssocItemList::cast(node.clone()) {
-        closing_token = item_list.r_curly_token()?;
-
-        let parent = item_list.syntax().parent()?;
-        match_ast! {
-            match parent {
-                ast::Impl(imp) => {
-                    let imp = sema.to_def(&imp)?;
-                    let ty = imp.self_ty(sema.db);
-                    let trait_ = imp.trait_(sema.db);
-                    let hint_text = match trait_ {
-                        Some(tr) => format!("impl {} for {}", tr.name(sema.db), ty.display_truncated(sema.db, config.max_length)),
-                        None => format!("impl {}", ty.display_truncated(sema.db, config.max_length)),
-                    };
-                    (hint_text, None)
-                },
-                ast::Trait(tr) => {
-                    (format!("trait {}", tr.name()?), tr.name().map(name))
-                },
-                _ => return None,
-            }
-        }
-    } else if let Some(list) = ast::ItemList::cast(node.clone()) {
-        closing_token = list.r_curly_token()?;
-
-        let module = ast::Module::cast(list.syntax().parent()?)?;
-        (format!("mod {}", module.name()?), module.name().map(name))
-    } else if let Some(block) = ast::BlockExpr::cast(node.clone()) {
-        closing_token = block.stmt_list()?.r_curly_token()?;
-
-        let parent = block.syntax().parent()?;
-        match_ast! {
-            match parent {
-                ast::Fn(it) => {
-                    // FIXME: this could include parameters, but `HirDisplay` prints too much info
-                    // and doesn't respect the max length either, so the hints end up way too long
-                    (format!("fn {}", it.name()?), it.name().map(name))
-                },
-                ast::Static(it) => (format!("static {}", it.name()?), it.name().map(name)),
-                ast::Const(it) => {
-                    if it.underscore_token().is_some() {
-                        ("const _".into(), None)
-                    } else {
-                        (format!("const {}", it.name()?), it.name().map(name))
-                    }
-                },
-                _ => return None,
-            }
-        }
-    } else if let Some(mac) = ast::MacroCall::cast(node.clone()) {
-        let last_token = mac.syntax().last_token()?;
-        if last_token.kind() != T![;] && last_token.kind() != SyntaxKind::R_CURLY {
-            return None;
-        }
-        closing_token = last_token;
-
-        (
-            format!("{}!", mac.path()?),
-            mac.path().and_then(|it| it.segment()).map(|it| it.syntax().text_range()),
-        )
-    } else {
-        return None;
-    };
-
-    if let Some(mut next) = closing_token.next_token() {
-        if next.kind() == T![;] {
-            if let Some(tok) = next.next_token() {
-                closing_token = next;
-                next = tok;
-            }
-        }
-        if !(next.kind() == SyntaxKind::WHITESPACE && next.text().contains('\n')) {
-            // Only display the hint if the `}` is the last token on the line
-            return None;
-        }
-    }
-
-    let mut lines = 1;
-    node.text().for_each_chunk(|s| lines += s.matches('\n').count());
-    if lines < min_lines {
-        return None;
-    }
-
-    let linked_location = name_range.map(|range| FileRange { file_id, range });
-    acc.push(InlayHint {
-        range: closing_token.text_range(),
-        kind: InlayKind::ClosingBraceHint,
-        label: InlayHintLabel { parts: vec![InlayHintLabelPart { text: label, linked_location }] },
-        tooltip: None, // provided by label part location
-    });
-
-    None
-}
-
-#[cfg(test)]
-mod tests {
-    use crate::{
-        inlay_hints::tests::{check_with_config, DISABLED_CONFIG},
-        InlayHintsConfig,
-    };
-
-    #[test]
-    fn hints_closing_brace() {
-        check_with_config(
-            InlayHintsConfig { closing_brace_hints_min_lines: Some(2), ..DISABLED_CONFIG },
-            r#"
-fn a() {}
-
-fn f() {
-} // no hint unless `}` is the last token on the line
-
-fn g() {
-  }
-//^ fn g
-
-fn h<T>(with: T, arguments: u8, ...) {
-  }
-//^ fn h
-
-trait Tr {
-    fn f();
-    fn g() {
-    }
-  //^ fn g
-  }
-//^ trait Tr
-impl Tr for () {
-  }
-//^ impl Tr for ()
-impl dyn Tr {
-  }
-//^ impl dyn Tr
-
-static S0: () = 0;
-static S1: () = {};
-static S2: () = {
- };
-//^ static S2
-const _: () = {
- };
-//^ const _
-
-mod m {
-  }
-//^ mod m
-
-m! {}
-m!();
-m!(
- );
-//^ m!
-
-m! {
-  }
-//^ m!
-
-fn f() {
-    let v = vec![
-    ];
-  }
-//^ fn f
-"#,
-        );
-    }
-}
diff --git a/crates/ide/src/inlay_hints/closure_ret.rs b/crates/ide/src/inlay_hints/closure_ret.rs
deleted file mode 100644
index de04f3ac75..0000000000
--- a/crates/ide/src/inlay_hints/closure_ret.rs
+++ /dev/null
@@ -1,51 +0,0 @@
-//! Implementation of "closure return type" inlay hints.
-use hir::{HirDisplay, Semantics};
-use ide_db::{base_db::FileId, famous_defs::FamousDefs, RootDatabase};
-use syntax::ast::{self, AstNode};
-
-use crate::{
-    inlay_hints::{closure_has_block_body, hint_iterator},
-    ClosureReturnTypeHints, InlayHint, InlayHintsConfig, InlayKind, InlayTooltip,
-};
-
-pub(super) fn hints(
-    acc: &mut Vec<InlayHint>,
-    sema: &Semantics<'_, RootDatabase>,
-    famous_defs: &FamousDefs<'_, '_>,
-    config: &InlayHintsConfig,
-    file_id: FileId,
-    closure: ast::ClosureExpr,
-) -> Option<()> {
-    if config.closure_return_type_hints == ClosureReturnTypeHints::Never {
-        return None;
-    }
-
-    if closure.ret_type().is_some() {
-        return None;
-    }
-
-    if !closure_has_block_body(&closure)
-        && config.closure_return_type_hints == ClosureReturnTypeHints::WithBlock
-    {
-        return None;
-    }
-
-    let param_list = closure.param_list()?;
-
-    let closure = sema.descend_node_into_attributes(closure.clone()).pop()?;
-    let ty = sema.type_of_expr(&ast::Expr::ClosureExpr(closure))?.adjusted();
-    let callable = ty.as_callable(sema.db)?;
-    let ty = callable.return_type();
-    if ty.is_unit() {
-        return None;
-    }
-    acc.push(InlayHint {
-        range: param_list.syntax().text_range(),
-        kind: InlayKind::ClosureReturnTypeHint,
-        label: hint_iterator(sema, &famous_defs, config, &ty)
-            .unwrap_or_else(|| ty.display_truncated(sema.db, config.max_length).to_string())
-            .into(),
-        tooltip: Some(InlayTooltip::HoverRanged(file_id, param_list.syntax().text_range())),
-    });
-    Some(())
-}
diff --git a/crates/ide/src/inlay_hints/fn_lifetime_fn.rs b/crates/ide/src/inlay_hints/fn_lifetime_fn.rs
deleted file mode 100644
index 1f5bcea63a..0000000000
--- a/crates/ide/src/inlay_hints/fn_lifetime_fn.rs
+++ /dev/null
@@ -1,301 +0,0 @@
-//! Implementation of "lifetime elision" inlay hints:
-//! ```no_run
-//! fn example/* <'0> */(a: &/* '0 */()) {}
-//! ```
-use ide_db::{syntax_helpers::node_ext::walk_ty, FxHashMap};
-use itertools::Itertools;
-use syntax::SmolStr;
-use syntax::{
-    ast::{self, AstNode, HasGenericParams, HasName},
-    SyntaxToken,
-};
-
-use crate::{InlayHint, InlayHintsConfig, InlayKind, InlayTooltip, LifetimeElisionHints};
-
-pub(super) fn hints(
-    acc: &mut Vec<InlayHint>,
-    config: &InlayHintsConfig,
-    func: ast::Fn,
-) -> Option<()> {
-    if config.lifetime_elision_hints == LifetimeElisionHints::Never {
-        return None;
-    }
-
-    let mk_lt_hint = |t: SyntaxToken, label: String| InlayHint {
-        range: t.text_range(),
-        kind: InlayKind::LifetimeHint,
-        label: label.into(),
-        tooltip: Some(InlayTooltip::String("Elided lifetime".into())),
-    };
-
-    let param_list = func.param_list()?;
-    let generic_param_list = func.generic_param_list();
-    let ret_type = func.ret_type();
-    let self_param = param_list.self_param().filter(|it| it.amp_token().is_some());
-
-    let is_elided = |lt: &Option<ast::Lifetime>| match lt {
-        Some(lt) => matches!(lt.text().as_str(), "'_"),
-        None => true,
-    };
-
-    let potential_lt_refs = {
-        let mut acc: Vec<_> = vec![];
-        if let Some(self_param) = &self_param {
-            let lifetime = self_param.lifetime();
-            let is_elided = is_elided(&lifetime);
-            acc.push((None, self_param.amp_token(), lifetime, is_elided));
-        }
-        param_list.params().filter_map(|it| Some((it.pat(), it.ty()?))).for_each(|(pat, ty)| {
-            // FIXME: check path types
-            walk_ty(&ty, &mut |ty| match ty {
-                ast::Type::RefType(r) => {
-                    let lifetime = r.lifetime();
-                    let is_elided = is_elided(&lifetime);
-                    acc.push((
-                        pat.as_ref().and_then(|it| match it {
-                            ast::Pat::IdentPat(p) => p.name(),
-                            _ => None,
-                        }),
-                        r.amp_token(),
-                        lifetime,
-                        is_elided,
-                    ))
-                }
-                _ => (),
-            })
-        });
-        acc
-    };
-
-    // allocate names
-    let mut gen_idx_name = {
-        let mut gen = (0u8..).map(|idx| match idx {
-            idx if idx < 10 => SmolStr::from_iter(['\'', (idx + 48) as char]),
-            idx => format!("'{idx}").into(),
-        });
-        move || gen.next().unwrap_or_default()
-    };
-    let mut allocated_lifetimes = vec![];
-
-    let mut used_names: FxHashMap<SmolStr, usize> =
-        match config.param_names_for_lifetime_elision_hints {
-            true => generic_param_list
-                .iter()
-                .flat_map(|gpl| gpl.lifetime_params())
-                .filter_map(|param| param.lifetime())
-                .filter_map(|lt| Some((SmolStr::from(lt.text().as_str().get(1..)?), 0)))
-                .collect(),
-            false => Default::default(),
-        };
-    {
-        let mut potential_lt_refs = potential_lt_refs.iter().filter(|&&(.., is_elided)| is_elided);
-        if let Some(_) = &self_param {
-            if let Some(_) = potential_lt_refs.next() {
-                allocated_lifetimes.push(if config.param_names_for_lifetime_elision_hints {
-                    // self can't be used as a lifetime, so no need to check for collisions
-                    "'self".into()
-                } else {
-                    gen_idx_name()
-                });
-            }
-        }
-        potential_lt_refs.for_each(|(name, ..)| {
-            let name = match name {
-                Some(it) if config.param_names_for_lifetime_elision_hints => {
-                    if let Some(c) = used_names.get_mut(it.text().as_str()) {
-                        *c += 1;
-                        SmolStr::from(format!("'{text}{c}", text = it.text().as_str()))
-                    } else {
-                        used_names.insert(it.text().as_str().into(), 0);
-                        SmolStr::from_iter(["\'", it.text().as_str()])
-                    }
-                }
-                _ => gen_idx_name(),
-            };
-            allocated_lifetimes.push(name);
-        });
-    }
-
-    // fetch output lifetime if elision rule applies
-    let output = match potential_lt_refs.as_slice() {
-        [(_, _, lifetime, _), ..] if self_param.is_some() || potential_lt_refs.len() == 1 => {
-            match lifetime {
-                Some(lt) => match lt.text().as_str() {
-                    "'_" => allocated_lifetimes.get(0).cloned(),
-                    "'static" => None,
-                    name => Some(name.into()),
-                },
-                None => allocated_lifetimes.get(0).cloned(),
-            }
-        }
-        [..] => None,
-    };
-
-    if allocated_lifetimes.is_empty() && output.is_none() {
-        return None;
-    }
-
-    // apply hints
-    // apply output if required
-    let mut is_trivial = true;
-    if let (Some(output_lt), Some(r)) = (&output, ret_type) {
-        if let Some(ty) = r.ty() {
-            walk_ty(&ty, &mut |ty| match ty {
-                ast::Type::RefType(ty) if ty.lifetime().is_none() => {
-                    if let Some(amp) = ty.amp_token() {
-                        is_trivial = false;
-                        acc.push(mk_lt_hint(amp, output_lt.to_string()));
-                    }
-                }
-                _ => (),
-            })
-        }
-    }
-
-    if config.lifetime_elision_hints == LifetimeElisionHints::SkipTrivial && is_trivial {
-        return None;
-    }
-
-    let mut a = allocated_lifetimes.iter();
-    for (_, amp_token, _, is_elided) in potential_lt_refs {
-        if is_elided {
-            let t = amp_token?;
-            let lt = a.next()?;
-            acc.push(mk_lt_hint(t, lt.to_string()));
-        }
-    }
-
-    // generate generic param list things
-    match (generic_param_list, allocated_lifetimes.as_slice()) {
-        (_, []) => (),
-        (Some(gpl), allocated_lifetimes) => {
-            let angle_tok = gpl.l_angle_token()?;
-            let is_empty = gpl.generic_params().next().is_none();
-            acc.push(InlayHint {
-                range: angle_tok.text_range(),
-                kind: InlayKind::LifetimeHint,
-                label: format!(
-                    "{}{}",
-                    allocated_lifetimes.iter().format(", "),
-                    if is_empty { "" } else { ", " }
-                )
-                .into(),
-                tooltip: Some(InlayTooltip::String("Elided lifetimes".into())),
-            });
-        }
-        (None, allocated_lifetimes) => acc.push(InlayHint {
-            range: func.name()?.syntax().text_range(),
-            kind: InlayKind::GenericParamListHint,
-            label: format!("<{}>", allocated_lifetimes.iter().format(", "),).into(),
-            tooltip: Some(InlayTooltip::String("Elided lifetimes".into())),
-        }),
-    }
-    Some(())
-}
-
-#[cfg(test)]
-mod tests {
-    use crate::{
-        inlay_hints::tests::{check, check_with_config, TEST_CONFIG},
-        InlayHintsConfig, LifetimeElisionHints,
-    };
-
-    #[test]
-    fn hints_lifetimes() {
-        check(
-            r#"
-fn empty() {}
-
-fn no_gpl(a: &()) {}
- //^^^^^^<'0>
-          // ^'0
-fn empty_gpl<>(a: &()) {}
-      //    ^'0   ^'0
-fn partial<'b>(a: &(), b: &'b ()) {}
-//        ^'0, $  ^'0
-fn partial<'a>(a: &'a (), b: &()) {}
-//        ^'0, $             ^'0
-
-fn single_ret(a: &()) -> &() {}
-// ^^^^^^^^^^<'0>
-              // ^'0     ^'0
-fn full_mul(a: &(), b: &()) {}
-// ^^^^^^^^<'0, '1>
-            // ^'0     ^'1
-
-fn foo<'c>(a: &'c ()) -> &() {}
-                      // ^'c
-
-fn nested_in(a: &   &X< &()>) {}
-// ^^^^^^^^^<'0, '1, '2>
-              //^'0 ^'1 ^'2
-fn nested_out(a: &()) -> &   &X< &()>{}
-// ^^^^^^^^^^<'0>
-               //^'0     ^'0 ^'0 ^'0
-
-impl () {
-    fn foo(&self) {}
-    // ^^^<'0>
-        // ^'0
-    fn foo(&self) -> &() {}
-    // ^^^<'0>
-        // ^'0       ^'0
-    fn foo(&self, a: &()) -> &() {}
-    // ^^^<'0, '1>
-        // ^'0       ^'1     ^'0
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn hints_lifetimes_named() {
-        check_with_config(
-            InlayHintsConfig { param_names_for_lifetime_elision_hints: true, ..TEST_CONFIG },
-            r#"
-fn nested_in<'named>(named: &        &X<      &()>) {}
-//          ^'named1, 'named2, 'named3, $
-                          //^'named1 ^'named2 ^'named3
-"#,
-        );
-    }
-
-    #[test]
-    fn hints_lifetimes_trivial_skip() {
-        check_with_config(
-            InlayHintsConfig {
-                lifetime_elision_hints: LifetimeElisionHints::SkipTrivial,
-                ..TEST_CONFIG
-            },
-            r#"
-fn no_gpl(a: &()) {}
-fn empty_gpl<>(a: &()) {}
-fn partial<'b>(a: &(), b: &'b ()) {}
-fn partial<'a>(a: &'a (), b: &()) {}
-
-fn single_ret(a: &()) -> &() {}
-// ^^^^^^^^^^<'0>
-              // ^'0     ^'0
-fn full_mul(a: &(), b: &()) {}
-
-fn foo<'c>(a: &'c ()) -> &() {}
-                      // ^'c
-
-fn nested_in(a: &   &X< &()>) {}
-fn nested_out(a: &()) -> &   &X< &()>{}
-// ^^^^^^^^^^<'0>
-               //^'0     ^'0 ^'0 ^'0
-
-impl () {
-    fn foo(&self) {}
-    fn foo(&self) -> &() {}
-    // ^^^<'0>
-        // ^'0       ^'0
-    fn foo(&self, a: &()) -> &() {}
-    // ^^^<'0, '1>
-        // ^'0       ^'1     ^'0
-}
-"#,
-        );
-    }
-}
diff --git a/crates/ide/src/inlay_hints/implicit_static.rs b/crates/ide/src/inlay_hints/implicit_static.rs
deleted file mode 100644
index 588a0e3b6a..0000000000
--- a/crates/ide/src/inlay_hints/implicit_static.rs
+++ /dev/null
@@ -1,75 +0,0 @@
-//! Implementation of "implicit static" inlay hints:
-//! ```no_run
-//! static S: &/* 'static */str = "";
-//! ```
-use either::Either;
-use syntax::{
-    ast::{self, AstNode},
-    SyntaxKind,
-};
-
-use crate::{InlayHint, InlayHintsConfig, InlayKind, InlayTooltip, LifetimeElisionHints};
-
-pub(super) fn hints(
-    acc: &mut Vec<InlayHint>,
-    config: &InlayHintsConfig,
-    statik_or_const: Either<ast::Static, ast::Const>,
-) -> Option<()> {
-    if config.lifetime_elision_hints != LifetimeElisionHints::Always {
-        return None;
-    }
-
-    if let Either::Right(it) = &statik_or_const {
-        if ast::AssocItemList::can_cast(
-            it.syntax().parent().map_or(SyntaxKind::EOF, |it| it.kind()),
-        ) {
-            return None;
-        }
-    }
-
-    if let Some(ast::Type::RefType(ty)) = statik_or_const.either(|it| it.ty(), |it| it.ty()) {
-        if ty.lifetime().is_none() {
-            let t = ty.amp_token()?;
-            acc.push(InlayHint {
-                range: t.text_range(),
-                kind: InlayKind::LifetimeHint,
-                label: "'static".to_owned().into(),
-                tooltip: Some(InlayTooltip::String("Elided static lifetime".into())),
-            });
-        }
-    }
-
-    Some(())
-}
-
-#[cfg(test)]
-mod tests {
-    use crate::{
-        inlay_hints::tests::{check_with_config, TEST_CONFIG},
-        InlayHintsConfig, LifetimeElisionHints,
-    };
-
-    #[test]
-    fn hints_lifetimes_static() {
-        check_with_config(
-            InlayHintsConfig {
-                lifetime_elision_hints: LifetimeElisionHints::Always,
-                ..TEST_CONFIG
-            },
-            r#"
-trait Trait {}
-static S: &str = "";
-//        ^'static
-const C: &str = "";
-//       ^'static
-const C: &dyn Trait = panic!();
-//       ^'static
-
-impl () {
-    const C: &str = "";
-    const C: &dyn Trait = panic!();
-}
-"#,
-        );
-    }
-}
diff --git a/crates/ide/src/inlay_hints/param_name.rs b/crates/ide/src/inlay_hints/param_name.rs
deleted file mode 100644
index ecee67632e..0000000000
--- a/crates/ide/src/inlay_hints/param_name.rs
+++ /dev/null
@@ -1,546 +0,0 @@
-//! Implementation of "param name" inlay hints:
-//! ```no_run
-//! fn max(x: i32, y: i32) -> i32 { x + y }
-//! _ = max(/*x*/4, /*y*/4);
-//! ```
-use either::Either;
-use hir::{Callable, Semantics};
-use ide_db::{base_db::FileRange, RootDatabase};
-
-use stdx::to_lower_snake_case;
-use syntax::ast::{self, AstNode, HasArgList, HasName, UnaryOp};
-
-use crate::{InlayHint, InlayHintsConfig, InlayKind, InlayTooltip};
-
-pub(super) fn hints(
-    acc: &mut Vec<InlayHint>,
-    sema: &Semantics<'_, RootDatabase>,
-    config: &InlayHintsConfig,
-    expr: ast::Expr,
-) -> Option<()> {
-    if !config.parameter_hints {
-        return None;
-    }
-
-    let (callable, arg_list) = get_callable(sema, &expr)?;
-    let hints = callable
-        .params(sema.db)
-        .into_iter()
-        .zip(arg_list.args())
-        .filter_map(|((param, _ty), arg)| {
-            // Only annotate hints for expressions that exist in the original file
-            let range = sema.original_range_opt(arg.syntax())?;
-            let (param_name, name_syntax) = match param.as_ref()? {
-                Either::Left(pat) => ("self".to_string(), pat.name()),
-                Either::Right(pat) => match pat {
-                    ast::Pat::IdentPat(it) => (it.name()?.to_string(), it.name()),
-                    _ => return None,
-                },
-            };
-            Some((name_syntax, param_name, arg, range))
-        })
-        .filter(|(_, param_name, arg, _)| {
-            !should_hide_param_name_hint(sema, &callable, param_name, arg)
-        })
-        .map(|(param, param_name, _, FileRange { range, .. })| {
-            let mut tooltip = None;
-            if let Some(name) = param {
-                if let hir::CallableKind::Function(f) = callable.kind() {
-                    // assert the file is cached so we can map out of macros
-                    if let Some(_) = sema.source(f) {
-                        tooltip = sema.original_range_opt(name.syntax());
-                    }
-                }
-            }
-
-            InlayHint {
-                range,
-                kind: InlayKind::ParameterHint,
-                label: param_name.into(),
-                tooltip: tooltip.map(|it| InlayTooltip::HoverOffset(it.file_id, it.range.start())),
-            }
-        });
-
-    acc.extend(hints);
-    Some(())
-}
-
-fn get_callable(
-    sema: &Semantics<'_, RootDatabase>,
-    expr: &ast::Expr,
-) -> Option<(hir::Callable, ast::ArgList)> {
-    match expr {
-        ast::Expr::CallExpr(expr) => {
-            let descended = sema.descend_node_into_attributes(expr.clone()).pop();
-            let expr = descended.as_ref().unwrap_or(expr);
-            sema.type_of_expr(&expr.expr()?)?.original.as_callable(sema.db).zip(expr.arg_list())
-        }
-        ast::Expr::MethodCallExpr(expr) => {
-            let descended = sema.descend_node_into_attributes(expr.clone()).pop();
-            let expr = descended.as_ref().unwrap_or(expr);
-            sema.resolve_method_call_as_callable(expr).zip(expr.arg_list())
-        }
-        _ => None,
-    }
-}
-
-fn should_hide_param_name_hint(
-    sema: &Semantics<'_, RootDatabase>,
-    callable: &hir::Callable,
-    param_name: &str,
-    argument: &ast::Expr,
-) -> bool {
-    // These are to be tested in the `parameter_hint_heuristics` test
-    // hide when:
-    // - the parameter name is a suffix of the function's name
-    // - the argument is a qualified constructing or call expression where the qualifier is an ADT
-    // - exact argument<->parameter match(ignoring leading underscore) or parameter is a prefix/suffix
-    //   of argument with _ splitting it off
-    // - param starts with `ra_fixture`
-    // - param is a well known name in a unary function
-
-    let param_name = param_name.trim_start_matches('_');
-    if param_name.is_empty() {
-        return true;
-    }
-
-    if matches!(argument, ast::Expr::PrefixExpr(prefix) if prefix.op_kind() == Some(UnaryOp::Not)) {
-        return false;
-    }
-
-    let fn_name = match callable.kind() {
-        hir::CallableKind::Function(it) => Some(it.name(sema.db).to_smol_str()),
-        _ => None,
-    };
-    let fn_name = fn_name.as_deref();
-    is_param_name_suffix_of_fn_name(param_name, callable, fn_name)
-        || is_argument_similar_to_param_name(argument, param_name)
-        || param_name.starts_with("ra_fixture")
-        || (callable.n_params() == 1 && is_obvious_param(param_name))
-        || is_adt_constructor_similar_to_param_name(sema, argument, param_name)
-}
-
-/// Hide the parameter name of a unary function if it is a `_` - prefixed suffix of the function's name, or equal.
-///
-/// `fn strip_suffix(suffix)` will be hidden.
-/// `fn stripsuffix(suffix)` will not be hidden.
-fn is_param_name_suffix_of_fn_name(
-    param_name: &str,
-    callable: &Callable,
-    fn_name: Option<&str>,
-) -> bool {
-    match (callable.n_params(), fn_name) {
-        (1, Some(function)) => {
-            function == param_name
-                || function
-                    .len()
-                    .checked_sub(param_name.len())
-                    .and_then(|at| function.is_char_boundary(at).then(|| function.split_at(at)))
-                    .map_or(false, |(prefix, suffix)| {
-                        suffix.eq_ignore_ascii_case(param_name) && prefix.ends_with('_')
-                    })
-        }
-        _ => false,
-    }
-}
-
-fn is_argument_similar_to_param_name(argument: &ast::Expr, param_name: &str) -> bool {
-    // check whether param_name and argument are the same or
-    // whether param_name is a prefix/suffix of argument(split at `_`)
-    let argument = match get_string_representation(argument) {
-        Some(argument) => argument,
-        None => return false,
-    };
-
-    // std is honestly too panic happy...
-    let str_split_at = |str: &str, at| str.is_char_boundary(at).then(|| argument.split_at(at));
-
-    let param_name = param_name.trim_start_matches('_');
-    let argument = argument.trim_start_matches('_');
-
-    match str_split_at(argument, param_name.len()) {
-        Some((prefix, rest)) if prefix.eq_ignore_ascii_case(param_name) => {
-            return rest.is_empty() || rest.starts_with('_');
-        }
-        _ => (),
-    }
-    match argument.len().checked_sub(param_name.len()).and_then(|at| str_split_at(argument, at)) {
-        Some((rest, suffix)) if param_name.eq_ignore_ascii_case(suffix) => {
-            return rest.is_empty() || rest.ends_with('_');
-        }
-        _ => (),
-    }
-    false
-}
-
-fn get_string_representation(expr: &ast::Expr) -> Option<String> {
-    match expr {
-        ast::Expr::MethodCallExpr(method_call_expr) => {
-            let name_ref = method_call_expr.name_ref()?;
-            match name_ref.text().as_str() {
-                "clone" | "as_ref" => method_call_expr.receiver().map(|rec| rec.to_string()),
-                name_ref => Some(name_ref.to_owned()),
-            }
-        }
-        ast::Expr::MacroExpr(macro_expr) => {
-            Some(macro_expr.macro_call()?.path()?.segment()?.to_string())
-        }
-        ast::Expr::FieldExpr(field_expr) => Some(field_expr.name_ref()?.to_string()),
-        ast::Expr::PathExpr(path_expr) => Some(path_expr.path()?.segment()?.to_string()),
-        ast::Expr::PrefixExpr(prefix_expr) => get_string_representation(&prefix_expr.expr()?),
-        ast::Expr::RefExpr(ref_expr) => get_string_representation(&ref_expr.expr()?),
-        ast::Expr::CastExpr(cast_expr) => get_string_representation(&cast_expr.expr()?),
-        _ => None,
-    }
-}
-
-fn is_obvious_param(param_name: &str) -> bool {
-    // avoid displaying hints for common functions like map, filter, etc.
-    // or other obvious words used in std
-    let is_obvious_param_name =
-        matches!(param_name, "predicate" | "value" | "pat" | "rhs" | "other");
-    param_name.len() == 1 || is_obvious_param_name
-}
-
-fn is_adt_constructor_similar_to_param_name(
-    sema: &Semantics<'_, RootDatabase>,
-    argument: &ast::Expr,
-    param_name: &str,
-) -> bool {
-    let path = match argument {
-        ast::Expr::CallExpr(c) => c.expr().and_then(|e| match e {
-            ast::Expr::PathExpr(p) => p.path(),
-            _ => None,
-        }),
-        ast::Expr::PathExpr(p) => p.path(),
-        ast::Expr::RecordExpr(r) => r.path(),
-        _ => return false,
-    };
-    let path = match path {
-        Some(it) => it,
-        None => return false,
-    };
-    (|| match sema.resolve_path(&path)? {
-        hir::PathResolution::Def(hir::ModuleDef::Adt(_)) => {
-            Some(to_lower_snake_case(&path.segment()?.name_ref()?.text()) == param_name)
-        }
-        hir::PathResolution::Def(hir::ModuleDef::Function(_) | hir::ModuleDef::Variant(_)) => {
-            if to_lower_snake_case(&path.segment()?.name_ref()?.text()) == param_name {
-                return Some(true);
-            }
-            let qual = path.qualifier()?;
-            match sema.resolve_path(&qual)? {
-                hir::PathResolution::Def(hir::ModuleDef::Adt(_)) => {
-                    Some(to_lower_snake_case(&qual.segment()?.name_ref()?.text()) == param_name)
-                }
-                _ => None,
-            }
-        }
-        _ => None,
-    })()
-    .unwrap_or(false)
-}
-
-#[cfg(test)]
-mod tests {
-    use crate::{
-        inlay_hints::tests::{check_with_config, DISABLED_CONFIG},
-        InlayHintsConfig,
-    };
-
-    #[track_caller]
-    fn check_params(ra_fixture: &str) {
-        check_with_config(
-            InlayHintsConfig { parameter_hints: true, ..DISABLED_CONFIG },
-            ra_fixture,
-        );
-    }
-
-    #[test]
-    fn param_hints_only() {
-        check_params(
-            r#"
-fn foo(a: i32, b: i32) -> i32 { a + b }
-fn main() {
-    let _x = foo(
-        4,
-      //^ a
-        4,
-      //^ b
-    );
-}"#,
-        );
-    }
-
-    #[test]
-    fn param_hints_on_closure() {
-        check_params(
-            r#"
-fn main() {
-    let clo = |a: u8, b: u8| a + b;
-    clo(
-        1,
-      //^ a
-        2,
-      //^ b
-    );
-}
-            "#,
-        );
-    }
-
-    #[test]
-    fn param_name_similar_to_fn_name_still_hints() {
-        check_params(
-            r#"
-fn max(x: i32, y: i32) -> i32 { x + y }
-fn main() {
-    let _x = max(
-        4,
-      //^ x
-        4,
-      //^ y
-    );
-}"#,
-        );
-    }
-
-    #[test]
-    fn param_name_similar_to_fn_name() {
-        check_params(
-            r#"
-fn param_with_underscore(with_underscore: i32) -> i32 { with_underscore }
-fn main() {
-    let _x = param_with_underscore(
-        4,
-    );
-}"#,
-        );
-        check_params(
-            r#"
-fn param_with_underscore(underscore: i32) -> i32 { underscore }
-fn main() {
-    let _x = param_with_underscore(
-        4,
-    );
-}"#,
-        );
-    }
-
-    #[test]
-    fn param_name_same_as_fn_name() {
-        check_params(
-            r#"
-fn foo(foo: i32) -> i32 { foo }
-fn main() {
-    let _x = foo(
-        4,
-    );
-}"#,
-        );
-    }
-
-    #[test]
-    fn never_hide_param_when_multiple_params() {
-        check_params(
-            r#"
-fn foo(foo: i32, bar: i32) -> i32 { bar + baz }
-fn main() {
-    let _x = foo(
-        4,
-      //^ foo
-        8,
-      //^ bar
-    );
-}"#,
-        );
-    }
-
-    #[test]
-    fn param_hints_look_through_as_ref_and_clone() {
-        check_params(
-            r#"
-fn foo(bar: i32, baz: f32) {}
-
-fn main() {
-    let bar = 3;
-    let baz = &"baz";
-    let fez = 1.0;
-    foo(bar.clone(), bar.clone());
-                   //^^^^^^^^^^^ baz
-    foo(bar.as_ref(), bar.as_ref());
-                    //^^^^^^^^^^^^ baz
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn self_param_hints() {
-        check_params(
-            r#"
-struct Foo;
-
-impl Foo {
-    fn foo(self: Self) {}
-    fn bar(self: &Self) {}
-}
-
-fn main() {
-    Foo::foo(Foo);
-           //^^^ self
-    Foo::bar(&Foo);
-           //^^^^ self
-}
-"#,
-        )
-    }
-
-    #[test]
-    fn param_name_hints_show_for_literals() {
-        check_params(
-            r#"pub fn test(a: i32, b: i32) -> [i32; 2] { [a, b] }
-fn main() {
-    test(
-        0xa_b,
-      //^^^^^ a
-        0xa_b,
-      //^^^^^ b
-    );
-}"#,
-        )
-    }
-
-    #[test]
-    fn function_call_parameter_hint() {
-        check_params(
-            r#"
-//- minicore: option
-struct FileId {}
-struct SmolStr {}
-
-struct TextRange {}
-struct SyntaxKind {}
-struct NavigationTarget {}
-
-struct Test {}
-
-impl Test {
-    fn method(&self, mut param: i32) -> i32 { param * 2 }
-
-    fn from_syntax(
-        file_id: FileId,
-        name: SmolStr,
-        focus_range: Option<TextRange>,
-        full_range: TextRange,
-        kind: SyntaxKind,
-        docs: Option<String>,
-    ) -> NavigationTarget {
-        NavigationTarget {}
-    }
-}
-
-fn test_func(mut foo: i32, bar: i32, msg: &str, _: i32, last: i32) -> i32 {
-    foo + bar
-}
-
-fn main() {
-    let not_literal = 1;
-    let _: i32 = test_func(1,    2,      "hello", 3,  not_literal);
-                         //^ foo ^ bar   ^^^^^^^ msg  ^^^^^^^^^^^ last
-    let t: Test = Test {};
-    t.method(123);
-           //^^^ param
-    Test::method(&t,      3456);
-               //^^ self  ^^^^ param
-    Test::from_syntax(
-        FileId {},
-        "impl".into(),
-      //^^^^^^^^^^^^^ name
-        None,
-      //^^^^ focus_range
-        TextRange {},
-      //^^^^^^^^^^^^ full_range
-        SyntaxKind {},
-      //^^^^^^^^^^^^^ kind
-        None,
-      //^^^^ docs
-    );
-}"#,
-        );
-    }
-
-    #[test]
-    fn parameter_hint_heuristics() {
-        check_params(
-            r#"
-fn check(ra_fixture_thing: &str) {}
-
-fn map(f: i32) {}
-fn filter(predicate: i32) {}
-
-fn strip_suffix(suffix: &str) {}
-fn stripsuffix(suffix: &str) {}
-fn same(same: u32) {}
-fn same2(_same2: u32) {}
-
-fn enum_matches_param_name(completion_kind: CompletionKind) {}
-
-fn foo(param: u32) {}
-fn bar(param_eter: u32) {}
-
-enum CompletionKind {
-    Keyword,
-}
-
-fn non_ident_pat((a, b): (u32, u32)) {}
-
-fn main() {
-    const PARAM: u32 = 0;
-    foo(PARAM);
-    foo(!PARAM);
-     // ^^^^^^ param
-    check("");
-
-    map(0);
-    filter(0);
-
-    strip_suffix("");
-    stripsuffix("");
-              //^^ suffix
-    same(0);
-    same2(0);
-
-    enum_matches_param_name(CompletionKind::Keyword);
-
-    let param = 0;
-    foo(param);
-    foo(param as _);
-    let param_end = 0;
-    foo(param_end);
-    let start_param = 0;
-    foo(start_param);
-    let param2 = 0;
-    foo(param2);
-      //^^^^^^ param
-
-    macro_rules! param {
-        () => {};
-    };
-    foo(param!());
-
-    let param_eter = 0;
-    bar(param_eter);
-    let param_eter_end = 0;
-    bar(param_eter_end);
-    let start_param_eter = 0;
-    bar(start_param_eter);
-    let param_eter2 = 0;
-    bar(param_eter2);
-      //^^^^^^^^^^^ param_eter
-
-    non_ident_pat((0, 0));
-}"#,
-        );
-    }
-}
diff --git a/crates/mbe/src/expander/matcher.rs b/crates/mbe/src/expander/matcher.rs
index b6c5c1026e..3f656df25f 100644
--- a/crates/mbe/src/expander/matcher.rs
+++ b/crates/mbe/src/expander/matcher.rs
@@ -698,16 +698,12 @@ fn match_meta_var(kind: MetaVarKind, input: &mut TtIter<'_>) -> ExpandResult<Opt
         MetaVarKind::Item => parser::PrefixEntryPoint::Item,
         MetaVarKind::Vis => parser::PrefixEntryPoint::Vis,
         MetaVarKind::Expr => {
-            // `expr` should not match underscores, let expressions, or inline const. The latter
-            // two are for [backwards compatibility][0].
+            // `expr` should not match underscores.
             // HACK: Macro expansion should not be done using "rollback and try another alternative".
-            // rustc [explicitly checks the next token][1].
-            // [0]: https://github.com/rust-lang/rust/issues/86730
-            // [1]: https://github.com/rust-lang/rust/blob/f0c4da499/compiler/rustc_expand/src/mbe/macro_parser.rs#L576
+            // rustc [explicitly checks the next token][0].
+            // [0]: https://github.com/rust-lang/rust/blob/f0c4da499/compiler/rustc_expand/src/mbe/macro_parser.rs#L576
             match input.peek_n(0) {
-                Some(tt::TokenTree::Leaf(tt::Leaf::Ident(it)))
-                    if it.text == "_" || it.text == "let" || it.text == "const" =>
-                {
+                Some(tt::TokenTree::Leaf(tt::Leaf::Ident(it))) if it.text == "_" => {
                     return ExpandResult::only_err(ExpandError::NoMatchingRule)
                 }
                 _ => {}
diff --git a/crates/parser/src/grammar/patterns.rs b/crates/parser/src/grammar/patterns.rs
index abcefffa23..bc1224af9b 100644
--- a/crates/parser/src/grammar/patterns.rs
+++ b/crates/parser/src/grammar/patterns.rs
@@ -62,50 +62,39 @@ fn pattern_r(p: &mut Parser<'_>, recovery_set: TokenSet) {
 }
 
 fn pattern_single_r(p: &mut Parser<'_>, recovery_set: TokenSet) {
-    // test range_pat
-    // fn main() {
-    //     match 92 {
-    //         0 ... 100 => (),
-    //         101 ..= 200 => (),
-    //         200 .. 301 => (),
-    //         302 .. => (),
-    //         ..= 303 => (),
-    //     }
-    //
-    //     match Some(10 as u8) {
-    //         Some(0) | None => (),
-    //         Some(1..) => (),
-    //         Some(..=2) => (),
-    //     }
-    //
-    //     match () {
-    //         S { a: 0 } => (),
-    //         S { a: 1.. } => (),
-    //         S { a: ..=2 } => (),
-    //     }
-    //
-    //     match () {
-    //         [0] => (),
-    //         [1..] => (),
-    //         [..=2] => (),
-    //     }
-    //
-    //     match (10 as u8, 5 as u8) {
-    //         (0, _) => (),
-    //         (1.., _) => (),
-    //         (..=2, _) => (),
-    //     }
-    // }
-
-    if p.at(T![..=]) {
-        let m = p.start();
-        p.bump(T![..=]);
-        atom_pat(p, recovery_set);
-        m.complete(p, RANGE_PAT);
-        return;
-    }
-
     if let Some(lhs) = atom_pat(p, recovery_set) {
+        // test range_pat
+        // fn main() {
+        //     match 92 {
+        //         0 ... 100 => (),
+        //         101 ..= 200 => (),
+        //         200 .. 301 => (),
+        //         302 .. => (),
+        //     }
+        //
+        //     match Some(10 as u8) {
+        //         Some(0) | None => (),
+        //         Some(1..) => ()
+        //     }
+        //
+        //     match () {
+        //         S { a: 0 } => (),
+        //         S { a: 1.. } => (),
+        //     }
+        //
+        //     match () {
+        //         [0] => (),
+        //         [1..] => (),
+        //     }
+        //
+        //     match (10 as u8, 5 as u8) {
+        //         (0, _) => (),
+        //         (1.., _) => ()
+        //     }
+        // }
+
+        // FIXME: support half_open_range_patterns (`..=2`),
+        // exclusive_range_pattern (`..5`) with missing lhs
         for range_op in [T![...], T![..=], T![..]] {
             if p.at(range_op) {
                 let m = lhs.precede(p);
@@ -126,21 +115,11 @@ fn pattern_single_r(p: &mut Parser<'_>, recovery_set: TokenSet) {
                 //             ^
                 // `[0..]`
                 //      ^
-                // `0 .. if`
-                //       ^
-                if matches!(
-                    p.current(),
-                    T![=] | T![,] | T![:] | T![')'] | T!['}'] | T![']'] | T![if]
-                ) {
+                if matches!(p.current(), T![=] | T![,] | T![:] | T![')'] | T!['}'] | T![']']) {
                     // test half_open_range_pat
                     // fn f() {
                     //     let 0 .. = 1u32;
                     //     let 0..: _ = 1u32;
-                    //
-                    //     match 42 {
-                    //         0 .. if true => (),
-                    //         _ => (),
-                    //     }
                     // }
                 } else {
                     atom_pat(p, recovery_set);
diff --git a/crates/parser/src/parser.rs b/crates/parser/src/parser.rs
index b621ebc37c..48d8350e07 100644
--- a/crates/parser/src/parser.rs
+++ b/crates/parser/src/parser.rs
@@ -162,7 +162,7 @@ impl<'t> Parser<'t> {
         Marker::new(pos)
     }
 
-    /// Consume the next token. Panics if the parser isn't currently at `kind`.
+    /// Consume the next token if `kind` matches.
     pub(crate) fn bump(&mut self, kind: SyntaxKind) {
         assert!(self.eat(kind));
     }
@@ -237,7 +237,6 @@ impl<'t> Parser<'t> {
 
     fn do_bump(&mut self, kind: SyntaxKind, n_raw_tokens: u8) {
         self.pos += n_raw_tokens as usize;
-        self.steps.set(0);
         self.push_event(Event::Token { kind, n_raw_tokens });
     }
 
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 835b37c98e..6c0d712a4f 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -1125,8 +1125,11 @@ impl Config {
         }
     }
 
-    pub fn flycheck(&self) -> FlycheckConfig {
-        match &self.data.checkOnSave_overrideCommand {
+    pub fn flycheck(&self) -> Option<FlycheckConfig> {
+        if !self.data.checkOnSave_enable {
+            return None;
+        }
+        let flycheck_config = match &self.data.checkOnSave_overrideCommand {
             Some(args) if !args.is_empty() => {
                 let mut args = args.clone();
                 let command = args.remove(0);
@@ -1180,11 +1183,8 @@ impl Config {
                 extra_args: self.data.checkOnSave_extraArgs.clone(),
                 extra_env: self.check_on_save_extra_env(),
             },
-        }
-    }
-
-    pub fn check_on_save(&self) -> bool {
-        self.data.checkOnSave_enable
+        };
+        Some(flycheck_config)
     }
 
     pub fn runnables(&self) -> RunnablesConfig {
diff --git a/crates/rust-analyzer/src/config/patch_old_style.rs b/crates/rust-analyzer/src/config/patch_old_style.rs
index 3b174a7193..472e2e0eee 100644
--- a/crates/rust-analyzer/src/config/patch_old_style.rs
+++ b/crates/rust-analyzer/src/config/patch_old_style.rs
@@ -116,7 +116,7 @@ pub(super) fn patch_json_for_outdated_configs(json: &mut Value) {
         copy.pointer("/completion/addCallParenthesis"),
     ) {
         (Some(Value::Bool(true)), Some(Value::Bool(true))) => json!("fill_arguments"),
-        (_, Some(Value::Bool(true))) => json!("add_parentheses"),
+        (Some(Value::Bool(true)), _) => json!("add_parentheses"),
         (Some(Value::Bool(false)), Some(Value::Bool(false))) => json!("none"),
         (_, _) => return,
     };
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs
index 2850bc2c3d..1604a02fb1 100644
--- a/crates/rust-analyzer/src/handlers.rs
+++ b/crates/rust-analyzer/src/handlers.rs
@@ -1788,15 +1788,14 @@ fn run_rustfmt(
     let file_id = from_proto::file_id(snap, &text_document.uri)?;
     let file = snap.analysis.file_text(file_id)?;
 
-    // Determine the edition of the crate the file belongs to (if there's multiple, we pick the
-    // highest edition).
-    let editions = snap
+    // find the edition of the package the file belongs to
+    // (if it belongs to multiple we'll just pick the first one and pray)
+    let edition = snap
         .analysis
         .relevant_crates_for(file_id)?
         .into_iter()
-        .map(|crate_id| snap.analysis.crate_edition(crate_id))
-        .collect::<Result<Vec<_>, _>>()?;
-    let edition = editions.iter().copied().max();
+        .find_map(|crate_id| snap.cargo_target_for_crate_root(crate_id))
+        .map(|(ws, target)| ws[ws[target].package].edition);
 
     let line_index = snap.file_line_index(file_id)?;
 
diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs
index 65620b4209..a1df0b6d7d 100644
--- a/crates/rust-analyzer/src/lsp_ext.rs
+++ b/crates/rust-analyzer/src/lsp_ext.rs
@@ -132,31 +132,12 @@ pub struct ExpandedMacro {
 
 pub enum CancelFlycheck {}
 
-impl Notification for CancelFlycheck {
+impl Request for CancelFlycheck {
     type Params = ();
+    type Result = ();
     const METHOD: &'static str = "rust-analyzer/cancelFlycheck";
 }
 
-pub enum RunFlycheck {}
-
-impl Notification for RunFlycheck {
-    type Params = RunFlycheckParams;
-    const METHOD: &'static str = "rust-analyzer/runFlycheck";
-}
-
-pub enum ClearFlycheck {}
-
-impl Notification for ClearFlycheck {
-    type Params = ();
-    const METHOD: &'static str = "rust-analyzer/clearFlycheck";
-}
-
-#[derive(Deserialize, Serialize, Debug)]
-#[serde(rename_all = "camelCase")]
-pub struct RunFlycheckParams {
-    pub text_document: Option<TextDocumentIdentifier>,
-}
-
 pub enum MatchingBrace {}
 
 impl Request for MatchingBrace {
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index 47776f734b..274588ce0e 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -581,7 +581,10 @@ impl GlobalState {
                 // When we're running multiple flychecks, we have to include a disambiguator in
                 // the title, or the editor complains. Note that this is a user-facing string.
                 let title = if self.flycheck.len() == 1 {
-                    format!("{}", self.config.flycheck())
+                    match self.config.flycheck() {
+                        Some(config) => format!("{}", config),
+                        None => "cargo check".to_string(),
+                    }
                 } else {
                     format!("cargo check (#{})", id + 1)
                 };
@@ -590,7 +593,7 @@ impl GlobalState {
                     state,
                     message,
                     None,
-                    Some(format!("rust-analyzer/flycheck/{}", id)),
+                    Some(format!("rust-analyzer/checkOnSave/{}", id)),
                 );
             }
         }
@@ -635,6 +638,7 @@ impl GlobalState {
             .on_sync_mut::<lsp_ext::ReloadWorkspace>(handlers::handle_workspace_reload)
             .on_sync_mut::<lsp_ext::MemoryUsage>(handlers::handle_memory_usage)
             .on_sync_mut::<lsp_ext::ShuffleCrateGraph>(handlers::handle_shuffle_crate_graph)
+            .on_sync_mut::<lsp_ext::CancelFlycheck>(handlers::handle_cancel_flycheck)
             .on_sync::<lsp_ext::JoinLines>(handlers::handle_join_lines)
             .on_sync::<lsp_ext::OnEnter>(handlers::handle_on_enter)
             .on_sync::<lsp_types::request::SelectionRangeRequest>(handlers::handle_selection_range)
@@ -699,88 +703,6 @@ impl GlobalState {
 
     /// Handles an incoming notification.
     fn on_notification(&mut self, not: Notification) -> Result<()> {
-        // FIXME: Move these implementations out into a module similar to on_request
-        fn run_flycheck(this: &mut GlobalState, vfs_path: VfsPath) -> bool {
-            let file_id = this.vfs.read().0.file_id(&vfs_path);
-            if let Some(file_id) = file_id {
-                let world = this.snapshot();
-                let mut updated = false;
-                let task = move || -> std::result::Result<(), ide::Cancelled> {
-                    // Trigger flychecks for all workspaces that depend on the saved file
-                    // Crates containing or depending on the saved file
-                    let crate_ids: Vec<_> = world
-                        .analysis
-                        .crates_for(file_id)?
-                        .into_iter()
-                        .flat_map(|id| world.analysis.transitive_rev_deps(id))
-                        .flatten()
-                        .sorted()
-                        .unique()
-                        .collect();
-
-                    let crate_root_paths: Vec<_> = crate_ids
-                        .iter()
-                        .filter_map(|&crate_id| {
-                            world
-                                .analysis
-                                .crate_root(crate_id)
-                                .map(|file_id| {
-                                    world
-                                        .file_id_to_file_path(file_id)
-                                        .as_path()
-                                        .map(ToOwned::to_owned)
-                                })
-                                .transpose()
-                        })
-                        .collect::<ide::Cancellable<_>>()?;
-                    let crate_root_paths: Vec<_> =
-                        crate_root_paths.iter().map(Deref::deref).collect();
-
-                    // Find all workspaces that have at least one target containing the saved file
-                    let workspace_ids =
-                        world.workspaces.iter().enumerate().filter(|(_, ws)| match ws {
-                            project_model::ProjectWorkspace::Cargo { cargo, .. } => {
-                                cargo.packages().any(|pkg| {
-                                    cargo[pkg].targets.iter().any(|&it| {
-                                        crate_root_paths.contains(&cargo[it].root.as_path())
-                                    })
-                                })
-                            }
-                            project_model::ProjectWorkspace::Json { project, .. } => project
-                                .crates()
-                                .any(|(c, _)| crate_ids.iter().any(|&crate_id| crate_id == c)),
-                            project_model::ProjectWorkspace::DetachedFiles { .. } => false,
-                        });
-
-                    // Find and trigger corresponding flychecks
-                    for flycheck in world.flycheck.iter() {
-                        for (id, _) in workspace_ids.clone() {
-                            if id == flycheck.id() {
-                                updated = true;
-                                flycheck.restart();
-                                continue;
-                            }
-                        }
-                    }
-                    // No specific flycheck was triggered, so let's trigger all of them.
-                    if !updated {
-                        for flycheck in world.flycheck.iter() {
-                            flycheck.restart();
-                        }
-                    }
-                    Ok(())
-                };
-                this.task_pool.handle.spawn_with_sender(move |_| {
-                    if let Err(e) = std::panic::catch_unwind(task) {
-                        tracing::error!("flycheck task panicked: {e:?}")
-                    }
-                });
-                true
-            } else {
-                false
-            }
-        }
-
         NotificationDispatcher { not: Some(not), global_state: self }
             .on::<lsp_types::notification::Cancel>(|this, params| {
                 let id: lsp_server::RequestId = match params.id {
@@ -792,7 +714,7 @@ impl GlobalState {
             })?
             .on::<lsp_types::notification::WorkDoneProgressCancel>(|this, params| {
                 if let lsp_types::NumberOrString::String(s) = &params.token {
-                    if let Some(id) = s.strip_prefix("rust-analyzer/flycheck/") {
+                    if let Some(id) = s.strip_prefix("rust-analyzer/checkOnSave/") {
                         if let Ok(id) = u32::from_str_radix(id, 10) {
                             if let Some(flycheck) = this.flycheck.get(id as usize) {
                                 flycheck.cancel();
@@ -821,7 +743,6 @@ impl GlobalState {
                 }
                 Ok(())
             })?
-            .on::<lsp_ext::CancelFlycheck>(handlers::handle_cancel_flycheck)?
             .on::<lsp_types::notification::DidChangeTextDocument>(|this, params| {
                 if let Ok(path) = from_proto::vfs_path(&params.text_document.uri) {
                     match this.mem_docs.get_mut(&path) {
@@ -861,24 +782,6 @@ impl GlobalState {
                 }
                 Ok(())
             })?
-            .on::<lsp_ext::ClearFlycheck>(|this, ()| {
-                this.diagnostics.clear_check_all();
-                Ok(())
-            })?
-            .on::<lsp_ext::RunFlycheck>(|this, params| {
-                if let Some(text_document) = params.text_document {
-                    if let Ok(vfs_path) = from_proto::vfs_path(&text_document.uri) {
-                        if run_flycheck(this, vfs_path) {
-                            return Ok(());
-                        }
-                    }
-                }
-                // No specific flycheck was triggered, so let's trigger all of them.
-                for flycheck in this.flycheck.iter() {
-                    flycheck.restart();
-                }
-                Ok(())
-            })?
             .on::<lsp_types::notification::DidSaveTextDocument>(|this, params| {
                 if let Ok(vfs_path) = from_proto::vfs_path(&params.text_document.uri) {
                     // Re-fetch workspaces if a workspace related file has changed
@@ -889,14 +792,89 @@ impl GlobalState {
                         }
                     }
 
-                    if !this.config.check_on_save() || run_flycheck(this, vfs_path) {
+                    let file_id = this.vfs.read().0.file_id(&vfs_path);
+                    if let Some(file_id) = file_id {
+                        let world = this.snapshot();
+                        let mut updated = false;
+                        let task = move || -> std::result::Result<(), ide::Cancelled> {
+                            // Trigger flychecks for all workspaces that depend on the saved file
+                            // Crates containing or depending on the saved file
+                            let crate_ids: Vec<_> = world
+                                .analysis
+                                .crates_for(file_id)?
+                                .into_iter()
+                                .flat_map(|id| world.analysis.transitive_rev_deps(id))
+                                .flatten()
+                                .sorted()
+                                .unique()
+                                .collect();
+
+                            let crate_root_paths: Vec<_> = crate_ids
+                                .iter()
+                                .filter_map(|&crate_id| {
+                                    world
+                                        .analysis
+                                        .crate_root(crate_id)
+                                        .map(|file_id| {
+                                            world
+                                                .file_id_to_file_path(file_id)
+                                                .as_path()
+                                                .map(ToOwned::to_owned)
+                                        })
+                                        .transpose()
+                                })
+                                .collect::<ide::Cancellable<_>>()?;
+                            let crate_root_paths: Vec<_> =
+                                crate_root_paths.iter().map(Deref::deref).collect();
+
+                            // Find all workspaces that have at least one target containing the saved file
+                            let workspace_ids =
+                                world.workspaces.iter().enumerate().filter(|(_, ws)| match ws {
+                                    project_model::ProjectWorkspace::Cargo { cargo, .. } => {
+                                        cargo.packages().any(|pkg| {
+                                            cargo[pkg].targets.iter().any(|&it| {
+                                                crate_root_paths.contains(&cargo[it].root.as_path())
+                                            })
+                                        })
+                                    }
+                                    project_model::ProjectWorkspace::Json { project, .. } => {
+                                        project.crates().any(|(c, _)| {
+                                            crate_ids.iter().any(|&crate_id| crate_id == c)
+                                        })
+                                    }
+                                    project_model::ProjectWorkspace::DetachedFiles { .. } => false,
+                                });
+
+                            // Find and trigger corresponding flychecks
+                            for flycheck in world.flycheck.iter() {
+                                for (id, _) in workspace_ids.clone() {
+                                    if id == flycheck.id() {
+                                        updated = true;
+                                        flycheck.restart();
+                                        continue;
+                                    }
+                                }
+                            }
+                            // No specific flycheck was triggered, so let's trigger all of them.
+                            if !updated {
+                                for flycheck in world.flycheck.iter() {
+                                    flycheck.restart();
+                                }
+                            }
+                            Ok(())
+                        };
+                        this.task_pool.handle.spawn_with_sender(move |_| {
+                            if let Err(e) = std::panic::catch_unwind(task) {
+                                tracing::error!("DidSaveTextDocument flycheck task panicked: {e:?}")
+                            }
+                        });
                         return Ok(());
                     }
-                } else if this.config.check_on_save() {
-                    // No specific flycheck was triggered, so let's trigger all of them.
-                    for flycheck in this.flycheck.iter() {
-                        flycheck.restart();
-                    }
+                }
+
+                // No specific flycheck was triggered, so let's trigger all of them.
+                for flycheck in this.flycheck.iter() {
+                    flycheck.restart();
                 }
                 Ok(())
             })?
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs
index 3e7664d7d3..748a189c20 100644
--- a/crates/rust-analyzer/src/reload.rs
+++ b/crates/rust-analyzer/src/reload.rs
@@ -449,7 +449,15 @@ impl GlobalState {
 
     fn reload_flycheck(&mut self) {
         let _p = profile::span("GlobalState::reload_flycheck");
-        let config = self.config.flycheck();
+        let config = match self.config.flycheck() {
+            Some(it) => it,
+            None => {
+                self.flycheck = Arc::new([]);
+                self.diagnostics.clear_check_all();
+                return;
+            }
+        };
+
         let sender = self.flycheck_sender.clone();
         let invocation_strategy = match config {
             FlycheckConfig::CargoCommand { .. } => flycheck::InvocationStrategy::PerWorkspace,
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index a7106acc78..3ce24bdd14 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -440,24 +440,22 @@ pub(crate) fn inlay_hint(
     Ok(lsp_types::InlayHint {
         position: match inlay_hint.kind {
             // before annotated thing
-            InlayKind::OpeningParenthesis
-            | InlayKind::ParameterHint
-            | InlayKind::AdjustmentHint
-            | InlayKind::BindingModeHint => position(line_index, inlay_hint.range.start()),
+            InlayKind::ParameterHint | InlayKind::AdjustmentHint | InlayKind::BindingModeHint => {
+                position(line_index, inlay_hint.range.start())
+            }
             // after annotated thing
             InlayKind::ClosureReturnTypeHint
             | InlayKind::TypeHint
             | InlayKind::ChainingHint
             | InlayKind::GenericParamListHint
-            | InlayKind::ClosingParenthesis
+            | InlayKind::AdjustmentHintClosingParenthesis
             | InlayKind::LifetimeHint
             | InlayKind::ClosingBraceHint => position(line_index, inlay_hint.range.end()),
         },
         padding_left: Some(match inlay_hint.kind {
             InlayKind::TypeHint => !render_colons,
             InlayKind::ChainingHint | InlayKind::ClosingBraceHint => true,
-            InlayKind::ClosingParenthesis
-            | InlayKind::OpeningParenthesis
+            InlayKind::AdjustmentHintClosingParenthesis
             | InlayKind::BindingModeHint
             | InlayKind::ClosureReturnTypeHint
             | InlayKind::GenericParamListHint
@@ -466,8 +464,7 @@ pub(crate) fn inlay_hint(
             | InlayKind::ParameterHint => false,
         }),
         padding_right: Some(match inlay_hint.kind {
-            InlayKind::ClosingParenthesis
-            | InlayKind::OpeningParenthesis
+            InlayKind::AdjustmentHintClosingParenthesis
             | InlayKind::ChainingHint
             | InlayKind::ClosureReturnTypeHint
             | InlayKind::GenericParamListHint
@@ -482,8 +479,7 @@ pub(crate) fn inlay_hint(
             InlayKind::ClosureReturnTypeHint | InlayKind::TypeHint | InlayKind::ChainingHint => {
                 Some(lsp_types::InlayHintKind::TYPE)
             }
-            InlayKind::ClosingParenthesis
-            | InlayKind::OpeningParenthesis
+            InlayKind::AdjustmentHintClosingParenthesis
             | InlayKind::BindingModeHint
             | InlayKind::GenericParamListHint
             | InlayKind::LifetimeHint
diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md
index 1bbb4c2323..5aced035d5 100644
--- a/docs/dev/lsp-extensions.md
+++ b/docs/dev/lsp-extensions.md
@@ -1,5 +1,5 @@
 <!---
-lsp_ext.rs hash: 45bd7985265725c5
+lsp_ext.rs hash: 61fe425627f9baaa
 
 If you need to change the above hash to make the test pass, please check if you
 need to adjust this doc as well and ping this issue:
@@ -459,45 +459,6 @@ Note that this functionality is intended primarily to inform the end user about
 In particular, it's valid for the client to completely ignore this extension.
 Clients are discouraged from but are allowed to use the `health` status to decide if it's worth sending a request to the server.
 
-### Controlling Flycheck
-
-The flycheck/checkOnSave feature can be controlled via notifications sent by the client to the server.
-
-**Method:** `rust-analyzer/runFlycheck`
-
-**Notification:**
-
-```typescript
-interface RunFlycheckParams {
-    /// The text document whose cargo workspace flycheck process should be started.
-    /// If the document is null or does not belong to a cargo workspace all flycheck processes will be started.
-    textDocument: lc.TextDocumentIdentifier | null;
-}
-```
-
-Triggers the flycheck processes.
-
-
-**Method:** `rust-analyzer/clearFlycheck`
-
-**Notification:**
-
-```typescript
-interface ClearFlycheckParams {}
-```
-
-Clears the flycheck diagnostics.
-
-**Method:** `rust-analyzer/cancelFlycheck`
-
-**Notification:**
-
-```typescript
-interface CancelFlycheckParams {}
-```
-
-Cancels all running flycheck processes.
-
 ## Syntax Tree
 
 **Method:** `rust-analyzer/syntaxTree`
diff --git a/editors/code/package.json b/editors/code/package.json
index f9b0e28dad..5b83701a68 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -246,16 +246,6 @@
                 "command": "rust-analyzer.cancelFlycheck",
                 "title": "Cancel running flychecks",
                 "category": "rust-analyzer"
-            },
-            {
-                "command": "rust-analyzer.runFlycheck",
-                "title": "Run flycheck",
-                "category": "rust-analyzer"
-            },
-            {
-                "command": "rust-analyzer.clearFlycheck",
-                "title": "Clear flycheck diagnostics",
-                "category": "rust-analyzer"
             }
         ],
         "keybindings": [
diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts
index cb4e13e2c6..312087e4cf 100644
--- a/editors/code/src/commands.ts
+++ b/editors/code/src/commands.ts
@@ -788,23 +788,8 @@ export function openDocs(ctx: CtxInit): Cmd {
 
 export function cancelFlycheck(ctx: CtxInit): Cmd {
     return async () => {
-        await ctx.client.sendNotification(ra.cancelFlycheck);
-    };
-}
-
-export function clearFlycheck(ctx: CtxInit): Cmd {
-    return async () => {
-        await ctx.client.sendNotification(ra.clearFlycheck);
-    };
-}
-
-export function runFlycheck(ctx: CtxInit): Cmd {
-    return async () => {
-        const editor = ctx.activeRustEditor;
         const client = ctx.client;
-        const params = editor ? { uri: editor.document.uri.toString() } : null;
-
-        await client.sendNotification(ra.runFlycheck, { textDocument: params });
+        await client.sendRequest(ra.cancelFlycheck);
     };
 }
 
@@ -812,12 +797,12 @@ export function resolveCodeAction(ctx: CtxInit): Cmd {
     return async (params: lc.CodeAction) => {
         const client = ctx.client;
         params.command = undefined;
-        const item = await client.sendRequest(lc.CodeActionResolveRequest.type, params);
+        const item = await client?.sendRequest(lc.CodeActionResolveRequest.type, params);
         if (!item?.edit) {
             return;
         }
         const itemEdit = item.edit;
-        const edit = await client.protocol2CodeConverter.asWorkspaceEdit(itemEdit);
+        const edit = await client?.protocol2CodeConverter.asWorkspaceEdit(itemEdit);
         // filter out all text edits and recreate the WorkspaceEdit without them so we can apply
         // snippet edits on our own
         const lcFileSystemEdit = {
diff --git a/editors/code/src/lsp_ext.ts b/editors/code/src/lsp_ext.ts
index 29349cc20f..875261c48a 100644
--- a/editors/code/src/lsp_ext.ts
+++ b/editors/code/src/lsp_ext.ts
@@ -79,11 +79,7 @@ export const relatedTests = new lc.RequestType<lc.TextDocumentPositionParams, Te
     "rust-analyzer/relatedTests"
 );
 
-export const cancelFlycheck = new lc.NotificationType0("rust-analyzer/cancelFlycheck");
-export const clearFlycheck = new lc.NotificationType0("rust-analyzer/clearFlycheck");
-export const runFlycheck = new lc.NotificationType<{
-    textDocument: lc.TextDocumentIdentifier | null;
-}>("rust-analyzer/runFlycheck");
+export const cancelFlycheck = new lc.RequestType0<void, void>("rust-analyzer/cancelFlycheck");
 
 // Experimental extensions
 
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts
index 9a9667b2cd..01a1a2db7f 100644
--- a/editors/code/src/main.ts
+++ b/editors/code/src/main.ts
@@ -150,8 +150,6 @@ function createCommands(): Record<string, CommandFactory> {
         moveItemUp: { enabled: commands.moveItemUp },
         moveItemDown: { enabled: commands.moveItemDown },
         cancelFlycheck: { enabled: commands.cancelFlycheck },
-        clearFlycheck: { enabled: commands.clearFlycheck },
-        runFlycheck: { enabled: commands.runFlycheck },
         ssr: { enabled: commands.ssr },
         serverVersion: { enabled: commands.serverVersion },
         // Internal commands which are invoked by the server.
diff --git a/xtask/src/release/changelog.rs b/xtask/src/release/changelog.rs
index cbf79b657c..2647f7794f 100644
--- a/xtask/src/release/changelog.rs
+++ b/xtask/src/release/changelog.rs
@@ -65,7 +65,6 @@ pub(crate) fn get_changelog(
         "\
 = Changelog #{}
 :sectanchors:
-:experimental:
 :page-layout: post
 
 Commit: commit:{}[] +


Test patch:
diff --git a/crates/hir-def/src/macro_expansion_tests/mbe/matching.rs b/crates/hir-def/src/macro_expansion_tests/mbe/matching.rs
index 26f16542cb..fc90c6e9f3 100644
--- a/crates/hir-def/src/macro_expansion_tests/mbe/matching.rs
+++ b/crates/hir-def/src/macro_expansion_tests/mbe/matching.rs
@@ -136,52 +136,3 @@ macro_rules! m { ($($i:ident)? $vis:vis) => () }
 "#]],
     )
 }
-
-// For this test and the one below, see rust-lang/rust#86730.
-#[test]
-fn expr_dont_match_let_expr() {
-    check(
-        r#"
-macro_rules! foo {
-    ($e:expr) => { $e }
-}
-
-fn test() {
-    foo!(let a = 3);
-}
-"#,
-        expect![[r#"
-macro_rules! foo {
-    ($e:expr) => { $e }
-}
-
-fn test() {
-    /* error: no rule matches input tokens */missing;
-}
-"#]],
-    );
-}
-
-#[test]
-fn expr_dont_match_inline_const() {
-    check(
-        r#"
-macro_rules! foo {
-    ($e:expr) => { $e }
-}
-
-fn test() {
-    foo!(const { 3 });
-}
-"#,
-        expect![[r#"
-macro_rules! foo {
-    ($e:expr) => { $e }
-}
-
-fn test() {
-    /* error: no rule matches input tokens */missing;
-}
-"#]],
-    );
-}
diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs
index 3580c45702..62404afc4b 100644
--- a/crates/ide/src/hover/tests.rs
+++ b/crates/ide/src/hover/tests.rs
@@ -4079,37 +4079,6 @@ const FOO$0: f64 = 1.0f64;
     );
 }
 
-#[test]
-fn hover_const_eval_in_generic_trait() {
-    // Doesn't compile, but we shouldn't crash.
-    check(
-        r#"
-trait Trait<T> {
-    const FOO: bool = false;
-}
-struct S<T>(T);
-impl<T> Trait<T> for S<T> {
-    const FOO: bool = true;
-}
-
-fn test() {
-    S::FOO$0;
-}
-"#,
-        expect![[r#"
-            *FOO*
-
-            ```rust
-            test
-            ```
-
-            ```rust
-            const FOO: bool = true
-            ```
-        "#]],
-    );
-}
-
 #[test]
 fn hover_const_pat() {
     check(
diff --git a/crates/parser/test_data/parser/inline/ok/0058_range_pat.rast b/crates/parser/test_data/parser/inline/ok/0058_range_pat.rast
index d9981c5071..cfef5d3f95 100644
--- a/crates/parser/test_data/parser/inline/ok/0058_range_pat.rast
+++ b/crates/parser/test_data/parser/inline/ok/0058_range_pat.rast
@@ -93,21 +93,6 @@ SOURCE_FILE
                   L_PAREN "("
                   R_PAREN ")"
                 COMMA ","
-              WHITESPACE "\n        "
-              MATCH_ARM
-                RANGE_PAT
-                  DOT2EQ "..="
-                  WHITESPACE " "
-                  LITERAL_PAT
-                    LITERAL
-                      INT_NUMBER "303"
-                WHITESPACE " "
-                FAT_ARROW "=>"
-                WHITESPACE " "
-                TUPLE_EXPR
-                  L_PAREN "("
-                  R_PAREN ")"
-                COMMA ","
               WHITESPACE "\n    "
               R_CURLY "}"
         WHITESPACE "\n\n    "
@@ -184,28 +169,6 @@ SOURCE_FILE
                 TUPLE_EXPR
                   L_PAREN "("
                   R_PAREN ")"
-                COMMA ","
-              WHITESPACE "\n        "
-              MATCH_ARM
-                TUPLE_STRUCT_PAT
-                  PATH
-                    PATH_SEGMENT
-                      NAME_REF
-                        IDENT "Some"
-                  L_PAREN "("
-                  RANGE_PAT
-                    DOT2EQ "..="
-                    LITERAL_PAT
-                      LITERAL
-                        INT_NUMBER "2"
-                  R_PAREN ")"
-                WHITESPACE " "
-                FAT_ARROW "=>"
-                WHITESPACE " "
-                TUPLE_EXPR
-                  L_PAREN "("
-                  R_PAREN ")"
-                COMMA ","
               WHITESPACE "\n    "
               R_CURLY "}"
         WHITESPACE "\n\n    "
@@ -277,36 +240,6 @@ SOURCE_FILE
                   L_PAREN "("
                   R_PAREN ")"
                 COMMA ","
-              WHITESPACE "\n        "
-              MATCH_ARM
-                RECORD_PAT
-                  PATH
-                    PATH_SEGMENT
-                      NAME_REF
-                        IDENT "S"
-                  WHITESPACE " "
-                  RECORD_PAT_FIELD_LIST
-                    L_CURLY "{"
-                    WHITESPACE " "
-                    RECORD_PAT_FIELD
-                      NAME_REF
-                        IDENT "a"
-                      COLON ":"
-                      WHITESPACE " "
-                      RANGE_PAT
-                        DOT2EQ "..="
-                        LITERAL_PAT
-                          LITERAL
-                            INT_NUMBER "2"
-                    WHITESPACE " "
-                    R_CURLY "}"
-                WHITESPACE " "
-                FAT_ARROW "=>"
-                WHITESPACE " "
-                TUPLE_EXPR
-                  L_PAREN "("
-                  R_PAREN ")"
-                COMMA ","
               WHITESPACE "\n    "
               R_CURLY "}"
         WHITESPACE "\n\n    "
@@ -352,23 +285,6 @@ SOURCE_FILE
                   L_PAREN "("
                   R_PAREN ")"
                 COMMA ","
-              WHITESPACE "\n        "
-              MATCH_ARM
-                SLICE_PAT
-                  L_BRACK "["
-                  RANGE_PAT
-                    DOT2EQ "..="
-                    LITERAL_PAT
-                      LITERAL
-                        INT_NUMBER "2"
-                  R_BRACK "]"
-                WHITESPACE " "
-                FAT_ARROW "=>"
-                WHITESPACE " "
-                TUPLE_EXPR
-                  L_PAREN "("
-                  R_PAREN ")"
-                COMMA ","
               WHITESPACE "\n    "
               R_CURLY "}"
         WHITESPACE "\n\n    "
@@ -444,28 +360,6 @@ SOURCE_FILE
               TUPLE_EXPR
                 L_PAREN "("
                 R_PAREN ")"
-              COMMA ","
-            WHITESPACE "\n        "
-            MATCH_ARM
-              TUPLE_PAT
-                L_PAREN "("
-                RANGE_PAT
-                  DOT2EQ "..="
-                  LITERAL_PAT
-                    LITERAL
-                      INT_NUMBER "2"
-                COMMA ","
-                WHITESPACE " "
-                WILDCARD_PAT
-                  UNDERSCORE "_"
-                R_PAREN ")"
-              WHITESPACE " "
-              FAT_ARROW "=>"
-              WHITESPACE " "
-              TUPLE_EXPR
-                L_PAREN "("
-                R_PAREN ")"
-              COMMA ","
             WHITESPACE "\n    "
             R_CURLY "}"
         WHITESPACE "\n"
diff --git a/crates/parser/test_data/parser/inline/ok/0058_range_pat.rs b/crates/parser/test_data/parser/inline/ok/0058_range_pat.rs
index b54354211d..2411d51096 100644
--- a/crates/parser/test_data/parser/inline/ok/0058_range_pat.rs
+++ b/crates/parser/test_data/parser/inline/ok/0058_range_pat.rs
@@ -4,30 +4,25 @@ fn main() {
         101 ..= 200 => (),
         200 .. 301 => (),
         302 .. => (),
-        ..= 303 => (),
     }
 
     match Some(10 as u8) {
         Some(0) | None => (),
-        Some(1..) => (),
-        Some(..=2) => (),
+        Some(1..) => ()
     }
 
     match () {
         S { a: 0 } => (),
         S { a: 1.. } => (),
-        S { a: ..=2 } => (),
     }
 
     match () {
         [0] => (),
         [1..] => (),
-        [..=2] => (),
     }
 
     match (10 as u8, 5 as u8) {
         (0, _) => (),
-        (1.., _) => (),
-        (..=2, _) => (),
+        (1.., _) => ()
     }
 }
diff --git a/crates/parser/test_data/parser/inline/ok/0166_half_open_range_pat.rast b/crates/parser/test_data/parser/inline/ok/0166_half_open_range_pat.rast
index c85a685991..4b401b60df 100644
--- a/crates/parser/test_data/parser/inline/ok/0166_half_open_range_pat.rast
+++ b/crates/parser/test_data/parser/inline/ok/0166_half_open_range_pat.rast
@@ -46,49 +46,6 @@ SOURCE_FILE
           LITERAL
             INT_NUMBER "1u32"
           SEMICOLON ";"
-        WHITESPACE "\n\n    "
-        MATCH_EXPR
-          MATCH_KW "match"
-          WHITESPACE " "
-          LITERAL
-            INT_NUMBER "42"
-          WHITESPACE " "
-          MATCH_ARM_LIST
-            L_CURLY "{"
-            WHITESPACE "\n        "
-            MATCH_ARM
-              RANGE_PAT
-                LITERAL_PAT
-                  LITERAL
-                    INT_NUMBER "0"
-                WHITESPACE " "
-                DOT2 ".."
-              WHITESPACE " "
-              MATCH_GUARD
-                IF_KW "if"
-                WHITESPACE " "
-                LITERAL
-                  TRUE_KW "true"
-              WHITESPACE " "
-              FAT_ARROW "=>"
-              WHITESPACE " "
-              TUPLE_EXPR
-                L_PAREN "("
-                R_PAREN ")"
-              COMMA ","
-            WHITESPACE "\n        "
-            MATCH_ARM
-              WILDCARD_PAT
-                UNDERSCORE "_"
-              WHITESPACE " "
-              FAT_ARROW "=>"
-              WHITESPACE " "
-              TUPLE_EXPR
-                L_PAREN "("
-                R_PAREN ")"
-              COMMA ","
-            WHITESPACE "\n    "
-            R_CURLY "}"
         WHITESPACE "\n"
         R_CURLY "}"
   WHITESPACE "\n"
diff --git a/crates/parser/test_data/parser/inline/ok/0166_half_open_range_pat.rs b/crates/parser/test_data/parser/inline/ok/0166_half_open_range_pat.rs
index f7e2d07922..c9386a221a 100644
--- a/crates/parser/test_data/parser/inline/ok/0166_half_open_range_pat.rs
+++ b/crates/parser/test_data/parser/inline/ok/0166_half_open_range_pat.rs
@@ -1,9 +1,4 @@
 fn f() {
     let 0 .. = 1u32;
     let 0..: _ = 1u32;
-
-    match 42 {
-        0 .. if true => (),
-        _ => (),
-    }
 }