Repository: rust-analyzer/rust-analyzer
Pull request #11789 (2022-03-22 15:31:31)

fix: Fix lifetime elision hint configuration key
This is inconsistent with the other configuration keys unfortunately, but this is an issue of how we structured them here in general.

I feel like we'll have to do a proper pass over all the configs, check them for consistency and then write down some documentation of how our configs should be laid out, then apply fixes to the inconsistencies all at once which will entail breaking some user configurations unfortunately since we won't be able to do proper fallback handling for some of these.

Fixes https://github.com/rust-analyzer/rust-analyzer/issues/11778

bors r+


Patch:
diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs
index bc43fb15ee..b5e7d5db63 100644
--- a/crates/hir/src/diagnostics.rs
+++ b/crates/hir/src/diagnostics.rs
@@ -5,7 +5,7 @@
 //! be expressed in terms of hir types themselves.
 use cfg::{CfgExpr, CfgOptions};
 use either::Either;
-use hir_def::path::ModPath;
+use hir_def::{path::ModPath, type_ref::Mutability};
 use hir_expand::{name::Name, HirFileId, InFile};
 use syntax::{ast, AstPtr, SyntaxNodePtr, TextRange};
 
@@ -28,6 +28,7 @@ macro_rules! diagnostics {
 }
 
 diagnostics![
+    AddReferenceHere,
     BreakOutsideOfLoop,
     InactiveCode,
     IncorrectCase,
@@ -37,10 +38,11 @@ diagnostics![
     MismatchedArgCount,
     MissingFields,
     MissingMatchArms,
+    MissingOkOrSomeInTailExpr,
     MissingUnsafe,
     NoSuchField,
+    RemoveThisSemicolon,
     ReplaceFilterMapNextWithFindMap,
-    TypeMismatch,
     UnimplementedBuiltinMacro,
     UnresolvedExternCrate,
     UnresolvedImport,
@@ -145,6 +147,19 @@ pub struct MismatchedArgCount {
     pub found: usize,
 }
 
+#[derive(Debug)]
+pub struct RemoveThisSemicolon {
+    pub expr: InFile<AstPtr<ast::Expr>>,
+}
+
+#[derive(Debug)]
+pub struct MissingOkOrSomeInTailExpr {
+    pub expr: InFile<AstPtr<ast::Expr>>,
+    // `Some` or `Ok` depending on whether the return type is Result or Option
+    pub required: String,
+    pub expected: Type,
+}
+
 #[derive(Debug)]
 pub struct MissingMatchArms {
     pub file: HirFileId,
@@ -152,11 +167,9 @@ pub struct MissingMatchArms {
 }
 
 #[derive(Debug)]
-pub struct TypeMismatch {
-    // FIXME: add mismatches in patterns as well
+pub struct AddReferenceHere {
     pub expr: InFile<AstPtr<ast::Expr>>,
-    pub expected: Type,
-    pub actual: Type,
+    pub mutability: Mutability,
 }
 
 pub use hir_ty::diagnostics::IncorrectCase;
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index f4e58d88ed..3c12907b82 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -58,6 +58,7 @@ use hir_ty::{
     consteval::{
         eval_const, unknown_const_as_generic, ComputedExpr, ConstEvalCtx, ConstEvalError, ConstExt,
     },
+    could_unify,
     diagnostics::BodyValidationDiagnostic,
     method_resolution::{self, TyFingerprint},
     primitive::UintTy,
@@ -84,11 +85,12 @@ use crate::db::{DefDatabase, HirDatabase};
 pub use crate::{
     attrs::{HasAttrs, Namespace},
     diagnostics::{
-        AnyDiagnostic, BreakOutsideOfLoop, InactiveCode, IncorrectCase, InvalidDeriveTarget,
-        MacroError, MalformedDerive, MismatchedArgCount, MissingFields, MissingMatchArms,
-        MissingUnsafe, NoSuchField, ReplaceFilterMapNextWithFindMap, TypeMismatch,
-        UnimplementedBuiltinMacro, UnresolvedExternCrate, UnresolvedImport, UnresolvedMacroCall,
-        UnresolvedModule, UnresolvedProcMacro,
+        AddReferenceHere, AnyDiagnostic, BreakOutsideOfLoop, InactiveCode, IncorrectCase,
+        InvalidDeriveTarget, MacroError, MalformedDerive, MismatchedArgCount, MissingFields,
+        MissingMatchArms, MissingOkOrSomeInTailExpr, MissingUnsafe, NoSuchField,
+        RemoveThisSemicolon, ReplaceFilterMapNextWithFindMap, UnimplementedBuiltinMacro,
+        UnresolvedExternCrate, UnresolvedImport, UnresolvedMacroCall, UnresolvedModule,
+        UnresolvedProcMacro,
     },
     has_source::HasSource,
     semantics::{PathResolution, Semantics, SemanticsScope, TypeInfo},
@@ -1003,24 +1005,6 @@ impl Adt {
         Type::from_def(db, id.module(db.upcast()).krate(), id)
     }
 
-    /// Turns this ADT into a type with the given type parameters. This isn't
-    /// the greatest API, FIXME find a better one.
-    pub fn ty_with_args(self, db: &dyn HirDatabase, args: &[Type]) -> Type {
-        let id = AdtId::from(self);
-        let mut it = args.iter().map(|t| t.ty.clone());
-        let ty = TyBuilder::def_ty(db, id.into())
-            .fill(|x| {
-                let r = it.next().unwrap_or_else(|| TyKind::Error.intern(Interner));
-                match x {
-                    ParamKind::Type => GenericArgData::Ty(r).intern(Interner),
-                    ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()),
-                }
-            })
-            .build();
-        let krate = id.module(db.upcast()).krate();
-        Type::new(db, krate, id, ty)
-    }
-
     pub fn module(self, db: &dyn HirDatabase) -> Module {
         match self {
             Adt::Struct(s) => s.module(db),
@@ -1036,14 +1020,6 @@ impl Adt {
             Adt::Enum(e) => e.name(db),
         }
     }
-
-    pub fn as_enum(&self) -> Option<Enum> {
-        if let Self::Enum(v) = self {
-            Some(*v)
-        } else {
-            None
-        }
-    }
 }
 
 impl HasVisibility for Adt {
@@ -1187,30 +1163,6 @@ impl DefWithBody {
                 }
             }
         }
-        for (expr, mismatch) in infer.expr_type_mismatches() {
-            let expr = match source_map.expr_syntax(expr) {
-                Ok(expr) => expr,
-                Err(SyntheticSyntax) => continue,
-            };
-            acc.push(
-                TypeMismatch {
-                    expr,
-                    expected: Type::new(
-                        db,
-                        krate,
-                        DefWithBodyId::from(self),
-                        mismatch.expected.clone(),
-                    ),
-                    actual: Type::new(
-                        db,
-                        krate,
-                        DefWithBodyId::from(self),
-                        mismatch.actual.clone(),
-                    ),
-                }
-                .into(),
-            );
-        }
 
         for expr in hir_ty::diagnostics::missing_unsafe(db, self.into()) {
             match source_map.expr_syntax(expr) {
@@ -1307,6 +1259,25 @@ impl DefWithBody {
                         Err(SyntheticSyntax) => (),
                     }
                 }
+                BodyValidationDiagnostic::RemoveThisSemicolon { expr } => {
+                    match source_map.expr_syntax(expr) {
+                        Ok(expr) => acc.push(RemoveThisSemicolon { expr }.into()),
+                        Err(SyntheticSyntax) => (),
+                    }
+                }
+                BodyValidationDiagnostic::MissingOkOrSomeInTailExpr { expr, required } => {
+                    match source_map.expr_syntax(expr) {
+                        Ok(expr) => acc.push(
+                            MissingOkOrSomeInTailExpr {
+                                expr,
+                                required,
+                                expected: self.body_type(db),
+                            }
+                            .into(),
+                        ),
+                        Err(SyntheticSyntax) => (),
+                    }
+                }
                 BodyValidationDiagnostic::MissingMatchArms { match_expr } => {
                     match source_map.expr_syntax(match_expr) {
                         Ok(source_ptr) => {
@@ -1328,6 +1299,12 @@ impl DefWithBody {
                         Err(SyntheticSyntax) => (),
                     }
                 }
+                BodyValidationDiagnostic::AddReferenceHere { arg_expr, mutability } => {
+                    match source_map.expr_syntax(arg_expr) {
+                        Ok(expr) => acc.push(AddReferenceHere { expr, mutability }.into()),
+                        Err(SyntheticSyntax) => (),
+                    }
+                }
             }
         }
 
@@ -2641,17 +2618,6 @@ impl Type {
         Type { krate, env: environment, ty }
     }
 
-    pub fn reference(inner: &Type, m: Mutability) -> Type {
-        inner.derived(
-            TyKind::Ref(
-                if m.is_mut() { hir_ty::Mutability::Mut } else { hir_ty::Mutability::Not },
-                hir_ty::static_lifetime(),
-                inner.ty.clone(),
-            )
-            .intern(Interner),
-        )
-    }
-
     fn new(db: &dyn HirDatabase, krate: CrateId, lexical_env: impl HasResolver, ty: Ty) -> Type {
         let resolver = lexical_env.resolver(db.upcast());
         let environment = resolver
@@ -2693,12 +2659,6 @@ impl Type {
         matches!(self.ty.kind(Interner), TyKind::Ref(..))
     }
 
-    pub fn as_reference(&self) -> Option<(Type, Mutability)> {
-        let (ty, _lt, m) = self.ty.as_reference()?;
-        let m = Mutability::from_mutable(matches!(m, hir_ty::Mutability::Mut));
-        Some((self.derived(ty.clone()), m))
-    }
-
     pub fn is_slice(&self) -> bool {
         matches!(self.ty.kind(Interner), TyKind::Slice(..))
     }
@@ -2940,7 +2900,7 @@ impl Type {
         self.autoderef_(db).map(move |ty| self.derived(ty))
     }
 
-    fn autoderef_<'a>(&'a self, db: &'a dyn HirDatabase) -> impl Iterator<Item = Ty> + 'a {
+    pub fn autoderef_<'a>(&'a self, db: &'a dyn HirDatabase) -> impl Iterator<Item = Ty> + 'a {
         // There should be no inference vars in types passed here
         let canonical = hir_ty::replace_errors_with_variables(&self.ty);
         let environment = self.env.clone();
@@ -3278,12 +3238,7 @@ impl Type {
 
     pub fn could_unify_with(&self, db: &dyn HirDatabase, other: &Type) -> bool {
         let tys = hir_ty::replace_errors_with_variables(&(self.ty.clone(), other.ty.clone()));
-        hir_ty::could_unify(db, self.env.clone(), &tys)
-    }
-
-    pub fn could_coerce_to(&self, db: &dyn HirDatabase, to: &Type) -> bool {
-        let tys = hir_ty::replace_errors_with_variables(&(self.ty.clone(), to.ty.clone()));
-        hir_ty::could_coerce(db, self.env.clone(), &tys)
+        could_unify(db, self.env.clone(), &tys)
     }
 }
 
diff --git a/crates/hir_def/src/type_ref.rs b/crates/hir_def/src/type_ref.rs
index 8e9336a0cc..c6c521f733 100644
--- a/crates/hir_def/src/type_ref.rs
+++ b/crates/hir_def/src/type_ref.rs
@@ -38,22 +38,6 @@ impl Mutability {
             Mutability::Mut => "mut ",
         }
     }
-
-    /// Returns `true` if the mutability is [`Mut`].
-    ///
-    /// [`Mut`]: Mutability::Mut
-    #[must_use]
-    pub fn is_mut(&self) -> bool {
-        matches!(self, Self::Mut)
-    }
-
-    /// Returns `true` if the mutability is [`Shared`].
-    ///
-    /// [`Shared`]: Mutability::Shared
-    #[must_use]
-    pub fn is_shared(&self) -> bool {
-        matches!(self, Self::Shared)
-    }
 }
 
 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
diff --git a/crates/hir_ty/src/diagnostics/expr.rs b/crates/hir_ty/src/diagnostics/expr.rs
index 71eb7e3995..b7d765c59b 100644
--- a/crates/hir_ty/src/diagnostics/expr.rs
+++ b/crates/hir_ty/src/diagnostics/expr.rs
@@ -4,7 +4,10 @@
 
 use std::sync::Arc;
 
-use hir_def::{path::path, resolver::HasResolver, AssocItemId, DefWithBodyId, HasModule};
+use hir_def::{
+    expr::Statement, path::path, resolver::HasResolver, type_ref::Mutability, AssocItemId,
+    DefWithBodyId, HasModule,
+};
 use hir_expand::name;
 use itertools::Either;
 use rustc_hash::FxHashSet;
@@ -17,7 +20,7 @@ use crate::{
         deconstruct_pat::DeconstructedPat,
         usefulness::{compute_match_usefulness, MatchCheckCtx},
     },
-    InferenceResult, Interner, TyExt,
+    AdtId, InferenceResult, Interner, Ty, TyExt, TyKind,
 };
 
 pub(crate) use hir_def::{
@@ -40,9 +43,20 @@ pub enum BodyValidationDiagnostic {
         expected: usize,
         found: usize,
     },
+    RemoveThisSemicolon {
+        expr: ExprId,
+    },
+    MissingOkOrSomeInTailExpr {
+        expr: ExprId,
+        required: String,
+    },
     MissingMatchArms {
         match_expr: ExprId,
     },
+    AddReferenceHere {
+        arg_expr: ExprId,
+        mutability: Mutability,
+    },
 }
 
 impl BodyValidationDiagnostic {
@@ -102,6 +116,30 @@ impl ExprValidator {
                 });
             }
         }
+        let body_expr = &body[body.body_expr];
+        if let Expr::Block { statements, tail, .. } = body_expr {
+            if let Some(t) = tail {
+                self.validate_results_in_tail_expr(body.body_expr, *t, db);
+            } else if let Some(Statement::Expr { expr: id, .. }) = statements.last() {
+                self.validate_missing_tail_expr(body.body_expr, *id);
+            }
+        }
+
+        let infer = &self.infer;
+        let diagnostics = &mut self.diagnostics;
+
+        infer
+            .expr_type_mismatches()
+            .filter_map(|(expr, mismatch)| {
+                let (expr_without_ref, mutability) =
+                    check_missing_refs(infer, expr, &mismatch.expected)?;
+
+                Some((expr_without_ref, mutability))
+            })
+            .for_each(|(arg_expr, mutability)| {
+                diagnostics
+                    .push(BodyValidationDiagnostic::AddReferenceHere { arg_expr, mutability });
+            });
     }
 
     fn validate_call(
@@ -292,6 +330,66 @@ impl ExprValidator {
         }
         pattern
     }
+
+    fn validate_results_in_tail_expr(&mut self, body_id: ExprId, id: ExprId, db: &dyn HirDatabase) {
+        // the mismatch will be on the whole block currently
+        let mismatch = match self.infer.type_mismatch_for_expr(body_id) {
+            Some(m) => m,
+            None => return,
+        };
+
+        let core_result_path = path![core::result::Result];
+        let core_option_path = path![core::option::Option];
+
+        let resolver = self.owner.resolver(db.upcast());
+        let core_result_enum = match resolver.resolve_known_enum(db.upcast(), &core_result_path) {
+            Some(it) => it,
+            _ => return,
+        };
+        let core_option_enum = match resolver.resolve_known_enum(db.upcast(), &core_option_path) {
+            Some(it) => it,
+            _ => return,
+        };
+
+        let (params, required) = match mismatch.expected.kind(Interner) {
+            TyKind::Adt(AdtId(hir_def::AdtId::EnumId(enum_id)), parameters)
+                if *enum_id == core_result_enum =>
+            {
+                (parameters, "Ok".to_string())
+            }
+            TyKind::Adt(AdtId(hir_def::AdtId::EnumId(enum_id)), parameters)
+                if *enum_id == core_option_enum =>
+            {
+                (parameters, "Some".to_string())
+            }
+            _ => return,
+        };
+
+        if params.len(Interner) > 0 && params.at(Interner, 0).ty(Interner) == Some(&mismatch.actual)
+        {
+            self.diagnostics
+                .push(BodyValidationDiagnostic::MissingOkOrSomeInTailExpr { expr: id, required });
+        }
+    }
+
+    fn validate_missing_tail_expr(&mut self, body_id: ExprId, possible_tail_id: ExprId) {
+        let mismatch = match self.infer.type_mismatch_for_expr(body_id) {
+            Some(m) => m,
+            None => return,
+        };
+
+        let possible_tail_ty = match self.infer.type_of_expr.get(possible_tail_id) {
+            Some(ty) => ty,
+            None => return,
+        };
+
+        if !mismatch.actual.is_unit() || mismatch.expected != *possible_tail_ty {
+            return;
+        }
+
+        self.diagnostics
+            .push(BodyValidationDiagnostic::RemoveThisSemicolon { expr: possible_tail_id });
+    }
 }
 
 struct FilterMapNextChecker {
@@ -425,3 +523,30 @@ fn types_of_subpatterns_do_match(pat: PatId, body: &Body, infer: &InferenceResul
     walk(pat, body, infer, &mut has_type_mismatches);
     !has_type_mismatches
 }
+
+fn check_missing_refs(
+    infer: &InferenceResult,
+    arg: ExprId,
+    param: &Ty,
+) -> Option<(ExprId, Mutability)> {
+    let arg_ty = infer.type_of_expr.get(arg)?;
+
+    let reference_one = arg_ty.as_reference();
+    let reference_two = param.as_reference();
+
+    match (reference_one, reference_two) {
+        (None, Some((referenced_ty, _, mutability))) if referenced_ty == arg_ty => {
+            Some((arg, Mutability::from_mutable(matches!(mutability, chalk_ir::Mutability::Mut))))
+        }
+        (None, Some((referenced_ty, _, mutability))) => match referenced_ty.kind(Interner) {
+            TyKind::Slice(subst) if matches!(arg_ty.kind(Interner), TyKind::Array(arr_subst, _) if arr_subst == subst) => {
+                Some((
+                    arg,
+                    Mutability::from_mutable(matches!(mutability, chalk_ir::Mutability::Mut)),
+                ))
+            }
+            _ => None,
+        },
+        _ => None,
+    }
+}
diff --git a/crates/hir_ty/src/infer.rs b/crates/hir_ty/src/infer.rs
index c91cdec183..442774d0be 100644
--- a/crates/hir_ty/src/infer.rs
+++ b/crates/hir_ty/src/infer.rs
@@ -44,8 +44,6 @@ use crate::{
 //
 // https://github.com/rust-lang/rust/issues/57411
 #[allow(unreachable_pub)]
-pub use coerce::could_coerce;
-#[allow(unreachable_pub)]
 pub use unify::could_unify;
 
 pub(crate) mod unify;
diff --git a/crates/hir_ty/src/infer/coerce.rs b/crates/hir_ty/src/infer/coerce.rs
index f54440bf5b..528e3ba882 100644
--- a/crates/hir_ty/src/infer/coerce.rs
+++ b/crates/hir_ty/src/infer/coerce.rs
@@ -5,26 +5,23 @@
 //! See <https://doc.rust-lang.org/nomicon/coercions.html> and
 //! `librustc_typeck/check/coercion.rs`.
 
-use std::{iter, sync::Arc};
+use std::iter;
 
-use chalk_ir::{cast::Cast, BoundVar, Goal, Mutability, TyVariableKind};
+use chalk_ir::{cast::Cast, Goal, Mutability, TyVariableKind};
 use hir_def::{expr::ExprId, lang_item::LangItemTarget};
 use stdx::always;
 use syntax::SmolStr;
 
 use crate::{
     autoderef::{Autoderef, AutoderefKind},
-    db::HirDatabase,
     infer::{
-        Adjust, Adjustment, AutoBorrow, InferOk, InferenceContext, OverloadedDeref, PointerCast,
-        TypeError, TypeMismatch,
+        Adjust, Adjustment, AutoBorrow, InferOk, InferResult, InferenceContext, OverloadedDeref,
+        PointerCast, TypeError, TypeMismatch,
     },
     static_lifetime, Canonical, DomainGoal, FnPointer, FnSig, Guidance, InEnvironment, Interner,
-    Solution, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind,
+    Solution, Substitution, Ty, TyBuilder, TyExt, TyKind,
 };
 
-use super::unify::InferenceTable;
-
 pub(crate) type CoerceResult = Result<InferOk<(Vec<Adjustment>, Ty)>, TypeError>;
 
 /// Do not require any adjustments, i.e. coerce `x -> x`.
@@ -87,8 +84,8 @@ impl CoerceMany {
         };
         if let Some(sig) = sig {
             let target_ty = TyKind::Function(sig.to_fn_ptr()).intern(Interner);
-            let result1 = ctx.table.coerce_inner(self.expected_ty.clone(), &target_ty);
-            let result2 = ctx.table.coerce_inner(expr_ty.clone(), &target_ty);
+            let result1 = ctx.coerce_inner(self.expected_ty.clone(), &target_ty);
+            let result2 = ctx.coerce_inner(expr_ty.clone(), &target_ty);
             if let (Ok(result1), Ok(result2)) = (result1, result2) {
                 ctx.table.register_infer_ok(result1);
                 ctx.table.register_infer_ok(result2);
@@ -121,45 +118,6 @@ impl CoerceMany {
     }
 }
 
-pub fn could_coerce(
-    db: &dyn HirDatabase,
-    env: Arc<TraitEnvironment>,
-    tys: &Canonical<(Ty, Ty)>,
-) -> bool {
-    coerce(db, env, tys).is_ok()
-}
-
-pub(crate) fn coerce(
-    db: &dyn HirDatabase,
-    env: Arc<TraitEnvironment>,
-    tys: &Canonical<(Ty, Ty)>,
-) -> Result<(Vec<Adjustment>, Ty), TypeError> {
-    let mut table = InferenceTable::new(db, env);
-    let vars = table.fresh_subst(tys.binders.as_slice(Interner));
-    let ty1_with_vars = vars.apply(tys.value.0.clone(), Interner);
-    let ty2_with_vars = vars.apply(tys.value.1.clone(), Interner);
-    let (adjustments, ty) = table.coerce(&ty1_with_vars, &ty2_with_vars)?;
-    // default any type vars that weren't unified back to their original bound vars
-    // (kind of hacky)
-    let find_var = |iv| {
-        vars.iter(Interner).position(|v| match v.interned() {
-            chalk_ir::GenericArgData::Ty(ty) => ty.inference_var(Interner),
-            chalk_ir::GenericArgData::Lifetime(lt) => lt.inference_var(Interner),
-            chalk_ir::GenericArgData::Const(c) => c.inference_var(Interner),
-        } == Some(iv))
-    };
-    let fallback = |iv, kind, default, binder| match kind {
-        chalk_ir::VariableKind::Ty(_ty_kind) => find_var(iv)
-            .map_or(default, |i| BoundVar::new(binder, i).to_ty(Interner).cast(Interner)),
-        chalk_ir::VariableKind::Lifetime => find_var(iv)
-            .map_or(default, |i| BoundVar::new(binder, i).to_lifetime(Interner).cast(Interner)),
-        chalk_ir::VariableKind::Const(ty) => find_var(iv)
-            .map_or(default, |i| BoundVar::new(binder, i).to_const(Interner, ty).cast(Interner)),
-    };
-    // FIXME also map the types in the adjustments
-    Ok((adjustments, table.resolve_with_fallback(ty, &fallback)))
-}
-
 impl<'a> InferenceContext<'a> {
     /// Unify two types, but may coerce the first one to the second one
     /// using "implicit coercion rules" if needed.
@@ -168,31 +126,16 @@ impl<'a> InferenceContext<'a> {
         expr: Option<ExprId>,
         from_ty: &Ty,
         to_ty: &Ty,
-    ) -> Result<Ty, TypeError> {
-        let from_ty = self.resolve_ty_shallow(from_ty);
-        let to_ty = self.resolve_ty_shallow(to_ty);
-        let (adjustments, ty) = self.table.coerce(&from_ty, &to_ty)?;
-        if let Some(expr) = expr {
-            self.write_expr_adj(expr, adjustments);
-        }
-        Ok(ty)
-    }
-}
-
-impl<'a> InferenceTable<'a> {
-    /// Unify two types, but may coerce the first one to the second one
-    /// using "implicit coercion rules" if needed.
-    pub(crate) fn coerce(
-        &mut self,
-        from_ty: &Ty,
-        to_ty: &Ty,
-    ) -> Result<(Vec<Adjustment>, Ty), TypeError> {
+    ) -> InferResult<Ty> {
         let from_ty = self.resolve_ty_shallow(from_ty);
         let to_ty = self.resolve_ty_shallow(to_ty);
         match self.coerce_inner(from_ty, &to_ty) {
             Ok(InferOk { value: (adjustments, ty), goals }) => {
-                self.register_infer_ok(InferOk { value: (), goals });
-                Ok((adjustments, ty))
+                if let Some(expr) = expr {
+                    self.write_expr_adj(expr, adjustments);
+                }
+                self.table.register_infer_ok(InferOk { value: (), goals });
+                Ok(InferOk { value: ty, goals: Vec::new() })
             }
             Err(e) => {
                 // FIXME deal with error
@@ -211,7 +154,7 @@ impl<'a> InferenceTable<'a> {
             //
             // here, we would coerce from `!` to `?T`.
             if let TyKind::InferenceVar(tv, TyVariableKind::General) = to_ty.kind(Interner) {
-                self.set_diverging(*tv, true);
+                self.table.set_diverging(*tv, true);
             }
             return success(simple(Adjust::NeverToAny)(to_ty.clone()), to_ty.clone(), vec![]);
         }
@@ -260,7 +203,8 @@ impl<'a> InferenceTable<'a> {
     where
         F: FnOnce(Ty) -> Vec<Adjustment>,
     {
-        self.try_unify(t1, t2)
+        self.table
+            .try_unify(t1, t2)
             .and_then(|InferOk { goals, .. }| success(f(t1.clone()), t1.clone(), goals))
     }
 
@@ -315,9 +259,9 @@ impl<'a> InferenceTable<'a> {
         // details of coercion errors though, so I think it's useful to leave
         // the structure like it is.
 
-        let snapshot = self.snapshot();
+        let snapshot = self.table.snapshot();
 
-        let mut autoderef = Autoderef::new(self, from_ty.clone());
+        let mut autoderef = Autoderef::new(&mut self.table, from_ty.clone());
         let mut first_error = None;
         let mut found = None;
 
@@ -373,7 +317,7 @@ impl<'a> InferenceTable<'a> {
         let InferOk { value: ty, goals } = match found {
             Some(d) => d,
             None => {
-                self.rollback_to(snapshot);
+                self.table.rollback_to(snapshot);
                 let err = first_error.expect("coerce_borrowed_pointer had no error");
                 return Err(err);
             }
@@ -569,7 +513,7 @@ impl<'a> InferenceTable<'a> {
         let coerce_from =
             reborrow.as_ref().map_or_else(|| from_ty.clone(), |(_, adj)| adj.target.clone());
 
-        let krate = self.trait_env.krate;
+        let krate = self.resolver.krate().unwrap();
         let coerce_unsized_trait =
             match self.db.lang_item(krate, SmolStr::new_inline("coerce_unsized")) {
                 Some(LangItemTarget::TraitId(trait_)) => trait_,
@@ -602,7 +546,7 @@ impl<'a> InferenceTable<'a> {
         match solution {
             Solution::Unique(v) => {
                 canonicalized.apply_solution(
-                    self,
+                    &mut self.table,
                     Canonical {
                         binders: v.binders,
                         // FIXME handle constraints
@@ -612,7 +556,7 @@ impl<'a> InferenceTable<'a> {
             }
             Solution::Ambig(Guidance::Definite(subst)) => {
                 // FIXME need to record an obligation here
-                canonicalized.apply_solution(self, subst)
+                canonicalized.apply_solution(&mut self.table, subst)
             }
             // FIXME actually we maybe should also accept unknown guidance here
             _ => return Err(TypeError),
diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs
index 43cff92f23..ebbce33e01 100644
--- a/crates/hir_ty/src/infer/expr.rs
+++ b/crates/hir_ty/src/infer/expr.rs
@@ -28,7 +28,7 @@ use crate::{
     lower::{
         const_or_path_to_chalk, generic_arg_to_chalk, lower_to_chalk_mutability, ParamLoweringMode,
     },
-    mapping::{from_chalk, ToChalk},
+    mapping::from_chalk,
     method_resolution,
     primitive::{self, UintTy},
     static_lifetime, to_chalk_trait_id,
@@ -67,7 +67,7 @@ impl<'a> InferenceContext<'a> {
         let ty = self.infer_expr_inner(expr, expected);
         if let Some(target) = expected.only_has_type(&mut self.table) {
             match self.coerce(Some(expr), &ty, &target) {
-                Ok(res) => res,
+                Ok(res) => res.value,
                 Err(_) => {
                     self.result
                         .type_mismatches
@@ -279,16 +279,14 @@ impl<'a> InferenceContext<'a> {
                 let callee_ty = self.infer_expr(*callee, &Expectation::none());
                 let mut derefs = Autoderef::new(&mut self.table, callee_ty.clone());
                 let mut res = None;
-                let mut derefed_callee = callee_ty.clone();
                 // manual loop to be able to access `derefs.table`
                 while let Some((callee_deref_ty, _)) = derefs.next() {
                     res = derefs.table.callable_sig(&callee_deref_ty, args.len());
                     if res.is_some() {
-                        derefed_callee = callee_deref_ty;
                         break;
                     }
                 }
-                let (param_tys, ret_ty) = match res {
+                let (param_tys, ret_ty): (Vec<Ty>, Ty) = match res {
                     Some(res) => {
                         let adjustments = auto_deref_adjust_steps(&derefs);
                         self.write_expr_adj(*callee, adjustments);
@@ -296,7 +294,6 @@ impl<'a> InferenceContext<'a> {
                     }
                     None => (Vec::new(), self.err_ty()),
                 };
-                let indices_to_skip = self.check_legacy_const_generics(derefed_callee, args);
                 self.register_obligations_for_call(&callee_ty);
 
                 let expected_inputs = self.expected_inputs_for_expected_output(
@@ -305,7 +302,7 @@ impl<'a> InferenceContext<'a> {
                     param_tys.clone(),
                 );
 
-                self.check_call_arguments(args, &expected_inputs, &param_tys, &indices_to_skip);
+                self.check_call_arguments(args, &expected_inputs, &param_tys);
                 self.normalize_associated_types_in(ret_ty)
             }
             Expr::MethodCall { receiver, args, method_name, generic_args } => self
@@ -955,7 +952,7 @@ impl<'a> InferenceContext<'a> {
         let expected_inputs =
             self.expected_inputs_for_expected_output(expected, ret_ty.clone(), param_tys.clone());
 
-        self.check_call_arguments(args, &expected_inputs, &param_tys, &[]);
+        self.check_call_arguments(args, &expected_inputs, &param_tys);
         self.normalize_associated_types_in(ret_ty)
     }
 
@@ -986,40 +983,24 @@ impl<'a> InferenceContext<'a> {
         }
     }
 
-    fn check_call_arguments(
-        &mut self,
-        args: &[ExprId],
-        expected_inputs: &[Ty],
-        param_tys: &[Ty],
-        skip_indices: &[u32],
-    ) {
+    fn check_call_arguments(&mut self, args: &[ExprId], expected_inputs: &[Ty], param_tys: &[Ty]) {
         // Quoting https://github.com/rust-lang/rust/blob/6ef275e6c3cb1384ec78128eceeb4963ff788dca/src/librustc_typeck/check/mod.rs#L3325 --
         // We do this in a pretty awful way: first we type-check any arguments
         // that are not closures, then we type-check the closures. This is so
         // that we have more information about the types of arguments when we
         // type-check the functions. This isn't really the right way to do this.
         for &check_closures in &[false, true] {
-            let mut skip_indices = skip_indices.into_iter().copied().fuse().peekable();
             let param_iter = param_tys.iter().cloned().chain(repeat(self.err_ty()));
             let expected_iter = expected_inputs
                 .iter()
                 .cloned()
                 .chain(param_iter.clone().skip(expected_inputs.len()));
-            for (idx, ((&arg, param_ty), expected_ty)) in
-                args.iter().zip(param_iter).zip(expected_iter).enumerate()
-            {
+            for ((&arg, param_ty), expected_ty) in args.iter().zip(param_iter).zip(expected_iter) {
                 let is_closure = matches!(&self.body[arg], Expr::Lambda { .. });
                 if is_closure != check_closures {
                     continue;
                 }
 
-                while skip_indices.peek().map_or(false, |i| *i < idx as u32) {
-                    skip_indices.next();
-                }
-                if skip_indices.peek().copied() == Some(idx as u32) {
-                    continue;
-                }
-
                 // the difference between param_ty and expected here is that
                 // expected is the parameter when the expected *return* type is
                 // taken into account. So in `let _: &[i32] = identity(&[1, 2])`
@@ -1159,49 +1140,6 @@ impl<'a> InferenceContext<'a> {
         }
     }
 
-    /// Returns the argument indices to skip.
-    fn check_legacy_const_generics(&mut self, callee: Ty, args: &[ExprId]) -> Vec<u32> {
-        let (func, subst) = match callee.kind(Interner) {
-            TyKind::FnDef(fn_id, subst) => {
-                let callable = CallableDefId::from_chalk(self.db, *fn_id);
-                let func = match callable {
-                    CallableDefId::FunctionId(f) => f,
-                    _ => return Vec::new(),
-                };
-                (func, subst)
-            }
-            _ => return Vec::new(),
-        };
-
-        let data = self.db.function_data(func);
-        if data.legacy_const_generics_indices.is_empty() {
-            return Vec::new();
-        }
-
-        // only use legacy const generics if the param count matches with them
-        if data.params.len() + data.legacy_const_generics_indices.len() != args.len() {
-            return Vec::new();
-        }
-
-        // check legacy const parameters
-        for (subst_idx, arg_idx) in data.legacy_const_generics_indices.iter().copied().enumerate() {
-            let arg = match subst.at(Interner, subst_idx).constant(Interner) {
-                Some(c) => c,
-                None => continue, // not a const parameter?
-            };
-            if arg_idx >= args.len() as u32 {
-                continue;
-            }
-            let _ty = arg.data(Interner).ty.clone();
-            let expected = Expectation::none(); // FIXME use actual const ty, when that is lowered correctly
-            self.infer_expr(args[arg_idx as usize], &expected);
-            // FIXME: evaluate and unify with the const
-        }
-        let mut indices = data.legacy_const_generics_indices.clone();
-        indices.sort();
-        indices
-    }
-
     fn builtin_binary_op_return_ty(&mut self, op: BinaryOp, lhs_ty: Ty, rhs_ty: Ty) -> Option<Ty> {
         let lhs_ty = self.resolve_ty_shallow(&lhs_ty);
         let rhs_ty = self.resolve_ty_shallow(&rhs_ty);
diff --git a/crates/hir_ty/src/infer/unify.rs b/crates/hir_ty/src/infer/unify.rs
index 84ca1660af..ef0675d59f 100644
--- a/crates/hir_ty/src/infer/unify.rs
+++ b/crates/hir_ty/src/infer/unify.rs
@@ -3,8 +3,8 @@
 use std::{fmt, mem, sync::Arc};
 
 use chalk_ir::{
-    cast::Cast, fold::Fold, interner::HasInterner, zip::Zip, CanonicalVarKind, FloatTy, IntTy,
-    NoSolution, TyVariableKind, UniverseIndex,
+    cast::Cast, fold::Fold, interner::HasInterner, zip::Zip, FloatTy, IntTy, NoSolution,
+    TyVariableKind, UniverseIndex,
 };
 use chalk_solve::infer::ParameterEnaVariableExt;
 use ena::unify::UnifyKey;
@@ -299,23 +299,11 @@ impl<'a> InferenceTable<'a> {
         self.resolve_with_fallback_inner(&mut Vec::new(), t, &fallback)
     }
 
-    pub(crate) fn fresh_subst(&mut self, binders: &[CanonicalVarKind<Interner>]) -> Substitution {
-        Substitution::from_iter(
-            Interner,
-            binders.iter().map(|kind| {
-                let param_infer_var =
-                    kind.map_ref(|&ui| self.var_unification_table.new_variable(ui));
-                param_infer_var.to_generic_arg(Interner)
-            }),
-        )
-    }
-
     pub(crate) fn instantiate_canonical<T>(&mut self, canonical: Canonical<T>) -> T::Result
     where
         T: HasInterner<Interner = Interner> + Fold<Interner> + std::fmt::Debug,
     {
-        let subst = self.fresh_subst(canonical.binders.as_slice(Interner));
-        subst.apply(canonical.value, Interner)
+        self.var_unification_table.instantiate_canonical(Interner, canonical)
     }
 
     fn resolve_with_fallback_inner<T>(
diff --git a/crates/hir_ty/src/lib.rs b/crates/hir_ty/src/lib.rs
index 945b4b0e4a..59e6fe2a04 100644
--- a/crates/hir_ty/src/lib.rs
+++ b/crates/hir_ty/src/lib.rs
@@ -51,7 +51,7 @@ pub use autoderef::autoderef;
 pub use builder::{ParamKind, TyBuilder};
 pub use chalk_ext::*;
 pub use infer::{
-    could_coerce, could_unify, Adjust, Adjustment, AutoBorrow, InferenceDiagnostic, InferenceResult,
+    could_unify, Adjust, Adjustment, AutoBorrow, InferenceDiagnostic, InferenceResult,
 };
 pub use interner::Interner;
 pub use lower::{
diff --git a/crates/ide_assists/src/handlers/add_missing_match_arms.rs b/crates/ide_assists/src/handlers/add_missing_match_arms.rs
index 0dc67ede36..2070cca142 100644
--- a/crates/ide_assists/src/handlers/add_missing_match_arms.rs
+++ b/crates/ide_assists/src/handlers/add_missing_match_arms.rs
@@ -78,7 +78,7 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext) ->
         Peekable<Box<dyn Iterator<Item = (ast::Pat, bool)>>>,
         bool,
     ) = if let Some(enum_def) = resolve_enum_def(&ctx.sema, &expr) {
-        let is_non_exhaustive = enum_def.is_non_exhaustive(ctx.db(), module.krate());
+        let is_non_exhaustive = enum_def.is_non_exhaustive(ctx.db());
 
         let variants = enum_def.variants(ctx.db());
 
@@ -104,7 +104,7 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext) ->
         (missing_pats.peekable(), is_non_exhaustive)
     } else if let Some(enum_defs) = resolve_tuple_of_enum_def(&ctx.sema, &expr) {
         let is_non_exhaustive =
-            enum_defs.iter().any(|enum_def| enum_def.is_non_exhaustive(ctx.db(), module.krate()));
+            enum_defs.iter().any(|enum_def| enum_def.is_non_exhaustive(ctx.db()));
 
         let mut n_arms = 1;
         let variants_of_enums: Vec<Vec<ExtendedVariant>> = enum_defs
@@ -301,11 +301,9 @@ fn lift_enum(e: hir::Enum) -> ExtendedEnum {
 }
 
 impl ExtendedEnum {
-    fn is_non_exhaustive(self, db: &RootDatabase, krate: Crate) -> bool {
+    fn is_non_exhaustive(self, db: &RootDatabase) -> bool {
         match self {
-            ExtendedEnum::Enum(e) => {
-                e.attrs(db).by_key("non_exhaustive").exists() && e.module(db).krate() != krate
-            }
+            ExtendedEnum::Enum(e) => e.attrs(db).by_key("non_exhaustive").exists(),
             _ => false,
         }
     }
@@ -1659,32 +1657,8 @@ fn foo(t: E) {
     }
 
     #[test]
-    fn ignores_non_exhaustive_for_crate_local_enums() {
-        check_assist(
-            add_missing_match_arms,
-            r#"
-#[non_exhaustive]
-enum E { A, B, }
-
-fn foo(t: E) {
-    match $0t {
-    }
-}"#,
-            r#"
-#[non_exhaustive]
-enum E { A, B, }
-
-fn foo(t: E) {
-    match t {
-        $0E::A => todo!(),
-        E::B => todo!(),
-    }
-}"#,
-        );
-    }
-
-    #[test]
-    fn ignores_doc_hidden_and_non_exhaustive_for_crate_local_enums() {
+    fn ignores_doc_hidden_for_crate_local_enums_but_not_non_exhaustive() {
+        cov_mark::check!(added_wildcard_pattern);
         check_assist(
             add_missing_match_arms,
             r#"
@@ -1703,6 +1677,7 @@ fn foo(t: E) {
     match t {
         $0E::A => todo!(),
         E::B => todo!(),
+        _ => todo!(),
     }
 }"#,
         );
diff --git a/crates/ide_diagnostics/src/handlers/add_reference_here.rs b/crates/ide_diagnostics/src/handlers/add_reference_here.rs
new file mode 100644
index 0000000000..db24fd6cce
--- /dev/null
+++ b/crates/ide_diagnostics/src/handlers/add_reference_here.rs
@@ -0,0 +1,163 @@
+use hir::db::AstDatabase;
+use ide_db::source_change::SourceChange;
+use syntax::AstNode;
+use text_edit::TextEdit;
+
+use crate::{fix, Assist, Diagnostic, DiagnosticsContext};
+
+// Diagnostic: add-reference-here
+//
+// This diagnostic is triggered when there's a missing referencing of expression.
+pub(crate) fn add_reference_here(
+    ctx: &DiagnosticsContext<'_>,
+    d: &hir::AddReferenceHere,
+) -> Diagnostic {
+    Diagnostic::new(
+        "add-reference-here",
+        "add reference here",
+        ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range,
+    )
+    .with_fixes(fixes(ctx, d))
+}
+
+fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::AddReferenceHere) -> Option<Vec<Assist>> {
+    let root = ctx.sema.db.parse_or_expand(d.expr.file_id)?;
+    let arg_expr = d.expr.value.to_node(&root);
+
+    let arg_with_ref = format!("&{}{}", d.mutability.as_keyword_for_ref(), arg_expr.syntax());
+
+    let arg_range = arg_expr.syntax().text_range();
+    let edit = TextEdit::replace(arg_range, arg_with_ref);
+    let source_change =
+        SourceChange::from_text_edit(d.expr.file_id.original_file(ctx.sema.db), edit);
+
+    Some(vec![fix("add_reference_here", "Add reference here", source_change, arg_range)])
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::tests::{check_diagnostics, check_fix};
+
+    #[test]
+    fn missing_reference() {
+        check_diagnostics(
+            r#"
+fn main() {
+    test(123);
+       //^^^ 💡 error: add reference here
+}
+fn test(arg: &i32) {}
+"#,
+        );
+    }
+
+    #[test]
+    fn test_add_reference_to_int() {
+        check_fix(
+            r#"
+fn main() {
+    test(123$0);
+}
+fn test(arg: &i32) {}
+            "#,
+            r#"
+fn main() {
+    test(&123);
+}
+fn test(arg: &i32) {}
+            "#,
+        );
+    }
+
+    #[test]
+    fn test_add_mutable_reference_to_int() {
+        check_fix(
+            r#"
+fn main() {
+    test($0123);
+}
+fn test(arg: &mut i32) {}
+            "#,
+            r#"
+fn main() {
+    test(&mut 123);
+}
+fn test(arg: &mut i32) {}
+            "#,
+        );
+    }
+
+    #[test]
+    fn test_add_reference_to_array() {
+        check_fix(
+            r#"
+fn main() {
+    test($0[1, 2, 3]);
+}
+fn test(arg: &[i32]) {}
+            "#,
+            r#"
+fn main() {
+    test(&[1, 2, 3]);
+}
+fn test(arg: &[i32]) {}
+            "#,
+        );
+    }
+
+    #[test]
+    fn test_add_reference_to_method_call() {
+        check_fix(
+            r#"
+fn main() {
+    Test.call_by_ref($0123);
+}
+struct Test;
+impl Test {
+    fn call_by_ref(&self, arg: &i32) {}
+}
+            "#,
+            r#"
+fn main() {
+    Test.call_by_ref(&123);
+}
+struct Test;
+impl Test {
+    fn call_by_ref(&self, arg: &i32) {}
+}
+            "#,
+        );
+    }
+
+    #[test]
+    fn test_add_reference_to_let_stmt() {
+        check_fix(
+            r#"
+fn main() {
+    let test: &i32 = $0123;
+}
+            "#,
+            r#"
+fn main() {
+    let test: &i32 = &123;
+}
+            "#,
+        );
+    }
+
+    #[test]
+    fn test_add_mutable_reference_to_let_stmt() {
+        check_fix(
+            r#"
+fn main() {
+    let test: &mut i32 = $0123;
+}
+            "#,
+            r#"
+fn main() {
+    let test: &mut i32 = &mut 123;
+}
+            "#,
+        );
+    }
+}
diff --git a/crates/ide_diagnostics/src/handlers/field_shorthand.rs b/crates/ide_diagnostics/src/handlers/field_shorthand.rs
index 2b71053625..33152e2845 100644
--- a/crates/ide_diagnostics/src/handlers/field_shorthand.rs
+++ b/crates/ide_diagnostics/src/handlers/field_shorthand.rs
@@ -108,13 +108,13 @@ mod tests {
         check_diagnostics(
             r#"
 struct A { a: &'static str }
-fn main() { A { a: "hello" }; }
+fn main() { A { a: "hello" } }
 "#,
         );
         check_diagnostics(
             r#"
 struct A(usize);
-fn main() { A { 0: 0 }; }
+fn main() { A { 0: 0 } }
 "#,
         );
 
@@ -123,14 +123,14 @@ fn main() { A { 0: 0 }; }
 struct A { a: &'static str }
 fn main() {
     let a = "haha";
-    A { a$0: a };
+    A { a$0: a }
 }
 "#,
             r#"
 struct A { a: &'static str }
 fn main() {
     let a = "haha";
-    A { a };
+    A { a }
 }
 "#,
         );
@@ -141,7 +141,7 @@ struct A { a: &'static str, b: &'static str }
 fn main() {
     let a = "haha";
     let b = "bb";
-    A { a$0: a, b };
+    A { a$0: a, b }
 }
 "#,
             r#"
@@ -149,7 +149,7 @@ struct A { a: &'static str, b: &'static str }
 fn main() {
     let a = "haha";
     let b = "bb";
-    A { a, b };
+    A { a, b }
 }
 "#,
         );
diff --git a/crates/ide_diagnostics/src/handlers/missing_match_arms.rs b/crates/ide_diagnostics/src/handlers/missing_match_arms.rs
index fe6a8683c1..6bdcd41a79 100644
--- a/crates/ide_diagnostics/src/handlers/missing_match_arms.rs
+++ b/crates/ide_diagnostics/src/handlers/missing_match_arms.rs
@@ -278,7 +278,6 @@ fn main() {
     match (true, false) {
         (true, false, true) => (),
         (true) => (),
-      // ^^^^  error: expected (bool, bool), found bool
     }
     match (true, false) { (true,) => {} }
     match (0) { () => () }
diff --git a/crates/ide_diagnostics/src/handlers/missing_ok_or_some_in_tail_expr.rs b/crates/ide_diagnostics/src/handlers/missing_ok_or_some_in_tail_expr.rs
new file mode 100644
index 0000000000..d5635ba8ba
--- /dev/null
+++ b/crates/ide_diagnostics/src/handlers/missing_ok_or_some_in_tail_expr.rs
@@ -0,0 +1,223 @@
+use hir::{db::AstDatabase, TypeInfo};
+use ide_db::{
+    assists::Assist, source_change::SourceChange, syntax_helpers::node_ext::for_each_tail_expr,
+};
+use syntax::AstNode;
+use text_edit::TextEdit;
+
+use crate::{fix, Diagnostic, DiagnosticsContext};
+
+// Diagnostic: missing-ok-or-some-in-tail-expr
+//
+// This diagnostic is triggered if a block that should return `Result` returns a value not wrapped in `Ok`,
+// or if a block that should return `Option` returns a value not wrapped in `Some`.
+//
+// Example:
+//
+// ```rust
+// fn foo() -> Result<u8, ()> {
+//     10
+// }
+// ```
+pub(crate) fn missing_ok_or_some_in_tail_expr(
+    ctx: &DiagnosticsContext<'_>,
+    d: &hir::MissingOkOrSomeInTailExpr,
+) -> Diagnostic {
+    Diagnostic::new(
+        "missing-ok-or-some-in-tail-expr",
+        format!("wrap return expression in {}", d.required),
+        ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range,
+    )
+    .with_fixes(fixes(ctx, d))
+}
+
+fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingOkOrSomeInTailExpr) -> Option<Vec<Assist>> {
+    let root = ctx.sema.db.parse_or_expand(d.expr.file_id)?;
+    let tail_expr = d.expr.value.to_node(&root);
+    let tail_expr_range = tail_expr.syntax().text_range();
+    let mut builder = TextEdit::builder();
+    for_each_tail_expr(&tail_expr, &mut |expr| {
+        if ctx.sema.type_of_expr(expr).map(TypeInfo::original).as_ref() != Some(&d.expected) {
+            builder.insert(expr.syntax().text_range().start(), format!("{}(", d.required));
+            builder.insert(expr.syntax().text_range().end(), ")".to_string());
+        }
+    });
+    let source_change =
+        SourceChange::from_text_edit(d.expr.file_id.original_file(ctx.sema.db), builder.finish());
+    let name = if d.required == "Ok" { "Wrap with Ok" } else { "Wrap with Some" };
+    Some(vec![fix("wrap_tail_expr", name, source_change, tail_expr_range)])
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::tests::{check_diagnostics, check_fix};
+
+    #[test]
+    fn test_wrap_return_type_option() {
+        check_fix(
+            r#"
+//- minicore: option, result
+fn div(x: i32, y: i32) -> Option<i32> {
+    if y == 0 {
+        return None;
+    }
+    x / y$0
+}
+"#,
+            r#"
+fn div(x: i32, y: i32) -> Option<i32> {
+    if y == 0 {
+        return None;
+    }
+    Some(x / y)
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn test_wrap_return_type_option_tails() {
+        check_fix(
+            r#"
+//- minicore: option, result
+fn div(x: i32, y: i32) -> Option<i32> {
+    if y == 0 {
+        0
+    } else if true {
+        100
+    } else {
+        None
+    }$0
+}
+"#,
+            r#"
+fn div(x: i32, y: i32) -> Option<i32> {
+    if y == 0 {
+        Some(0)
+    } else if true {
+        Some(100)
+    } else {
+        None
+    }
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn test_wrap_return_type() {
+        check_fix(
+            r#"
+//- minicore: option, result
+fn div(x: i32, y: i32) -> Result<i32, ()> {
+    if y == 0 {
+        return Err(());
+    }
+    x / y$0
+}
+"#,
+            r#"
+fn div(x: i32, y: i32) -> Result<i32, ()> {
+    if y == 0 {
+        return Err(());
+    }
+    Ok(x / y)
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn test_wrap_return_type_handles_generic_functions() {
+        check_fix(
+            r#"
+//- minicore: option, result
+fn div<T>(x: T) -> Result<T, i32> {
+    if x == 0 {
+        return Err(7);
+    }
+    $0x
+}
+"#,
+            r#"
+fn div<T>(x: T) -> Result<T, i32> {
+    if x == 0 {
+        return Err(7);
+    }
+    Ok(x)
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn test_wrap_return_type_handles_type_aliases() {
+        check_fix(
+            r#"
+//- minicore: option, result
+type MyResult<T> = Result<T, ()>;
+
+fn div(x: i32, y: i32) -> MyResult<i32> {
+    if y == 0 {
+        return Err(());
+    }
+    x $0/ y
+}
+"#,
+            r#"
+type MyResult<T> = Result<T, ()>;
+
+fn div(x: i32, y: i32) -> MyResult<i32> {
+    if y == 0 {
+        return Err(());
+    }
+    Ok(x / y)
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn test_in_const_and_static() {
+        check_fix(
+            r#"
+//- minicore: option, result
+static A: Option<()> = {($0)};
+            "#,
+            r#"
+static A: Option<()> = {Some(())};
+            "#,
+        );
+        check_fix(
+            r#"
+//- minicore: option, result
+const _: Option<()> = {($0)};
+            "#,
+            r#"
+const _: Option<()> = {Some(())};
+            "#,
+        );
+    }
+
+    #[test]
+    fn test_wrap_return_type_not_applicable_when_expr_type_does_not_match_ok_type() {
+        check_diagnostics(
+            r#"
+//- minicore: option, result
+fn foo() -> Result<(), i32> { 0 }
+"#,
+        );
+    }
+
+    #[test]
+    fn test_wrap_return_type_not_applicable_when_return_type_is_not_result_or_option() {
+        check_diagnostics(
+            r#"
+//- minicore: option, result
+enum SomeOtherEnum { Ok(i32), Err(String) }
+
+fn foo() -> SomeOtherEnum { 0 }
+"#,
+        );
+    }
+}
diff --git a/crates/ide_diagnostics/src/handlers/remove_this_semicolon.rs b/crates/ide_diagnostics/src/handlers/remove_this_semicolon.rs
new file mode 100644
index 0000000000..141fbc42fa
--- /dev/null
+++ b/crates/ide_diagnostics/src/handlers/remove_this_semicolon.rs
@@ -0,0 +1,76 @@
+use ide_db::{
+    base_db::{FileLoader, FileRange},
+    source_change::SourceChange,
+};
+use syntax::{TextRange, TextSize};
+use text_edit::TextEdit;
+
+use crate::{fix, Assist, Diagnostic, DiagnosticsContext};
+
+// Diagnostic: remove-this-semicolon
+//
+// This diagnostic is triggered when there's an erroneous `;` at the end of the block.
+pub(crate) fn remove_this_semicolon(
+    ctx: &DiagnosticsContext<'_>,
+    d: &hir::RemoveThisSemicolon,
+) -> Diagnostic {
+    Diagnostic::new(
+        "remove-this-semicolon",
+        "remove this semicolon",
+        semicolon_range(ctx, d).unwrap_or_else(|it| it).range,
+    )
+    .with_fixes(fixes(ctx, d))
+}
+
+fn semicolon_range(
+    ctx: &DiagnosticsContext<'_>,
+    d: &hir::RemoveThisSemicolon,
+) -> Result<FileRange, FileRange> {
+    let expr_range = ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into()));
+    let file_text = ctx.sema.db.file_text(expr_range.file_id);
+    let range_end: usize = expr_range.range.end().into();
+    // FIXME: This doesn't handle whitespace and comments, but handling those in
+    // the presence of macros might prove tricky...
+    if file_text[range_end..].starts_with(';') {
+        Ok(FileRange {
+            file_id: expr_range.file_id,
+            range: TextRange::at(expr_range.range.end(), TextSize::of(';')),
+        })
+    } else {
+        Err(expr_range)
+    }
+}
+
+fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::RemoveThisSemicolon) -> Option<Vec<Assist>> {
+    let semicolon_range = semicolon_range(ctx, d).ok()?;
+
+    let edit = TextEdit::delete(semicolon_range.range);
+    let source_change = SourceChange::from_text_edit(semicolon_range.file_id, edit);
+
+    Some(vec![fix(
+        "remove_semicolon",
+        "Remove this semicolon",
+        source_change,
+        semicolon_range.range,
+    )])
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::tests::{check_diagnostics, check_fix};
+
+    #[test]
+    fn missing_semicolon() {
+        check_diagnostics(
+            r#"
+fn test() -> i32 { 123; }
+                    //^ 💡 error: remove this semicolon
+"#,
+        );
+    }
+
+    #[test]
+    fn remove_semicolon() {
+        check_fix(r#"fn f() -> i32 { 92$0; }"#, r#"fn f() -> i32 { 92 }"#);
+    }
+}
diff --git a/crates/ide_diagnostics/src/handlers/type_mismatch.rs b/crates/ide_diagnostics/src/handlers/type_mismatch.rs
deleted file mode 100644
index 040dcbd1d9..0000000000
--- a/crates/ide_diagnostics/src/handlers/type_mismatch.rs
+++ /dev/null
@@ -1,476 +0,0 @@
-use hir::{db::AstDatabase, HirDisplay, Type, TypeInfo};
-use ide_db::{
-    famous_defs::FamousDefs, source_change::SourceChange,
-    syntax_helpers::node_ext::for_each_tail_expr,
-};
-use syntax::{
-    ast::{BlockExpr, ExprStmt},
-    AstNode,
-};
-use text_edit::TextEdit;
-
-use crate::{fix, Assist, Diagnostic, DiagnosticsContext};
-
-// Diagnostic: type-mismatch
-//
-// This diagnostic is triggered when the type of an expression does not match
-// the expected type.
-pub(crate) fn type_mismatch(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch) -> Diagnostic {
-    let mut diag = Diagnostic::new(
-        "type-mismatch",
-        format!(
-            "expected {}, found {}",
-            d.expected.display(ctx.sema.db),
-            d.actual.display(ctx.sema.db)
-        ),
-        ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range,
-    )
-    .with_fixes(fixes(ctx, d));
-    if diag.fixes.is_none() {
-        diag.experimental = true;
-    }
-    diag
-}
-
-fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch) -> Option<Vec<Assist>> {
-    let mut fixes = Vec::new();
-
-    add_reference(ctx, d, &mut fixes);
-    add_missing_ok_or_some(ctx, d, &mut fixes);
-    remove_semicolon(ctx, d, &mut fixes);
-
-    if fixes.is_empty() {
-        None
-    } else {
-        Some(fixes)
-    }
-}
-
-fn add_reference(
-    ctx: &DiagnosticsContext<'_>,
-    d: &hir::TypeMismatch,
-    acc: &mut Vec<Assist>,
-) -> Option<()> {
-    let root = ctx.sema.db.parse_or_expand(d.expr.file_id)?;
-    let expr_node = d.expr.value.to_node(&root);
-
-    let range = ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range;
-
-    let (_, mutability) = d.expected.as_reference()?;
-    let actual_with_ref = Type::reference(&d.actual, mutability);
-    if !actual_with_ref.could_coerce_to(ctx.sema.db, &d.expected) {
-        return None;
-    }
-
-    let ampersands = format!("&{}", mutability.as_keyword_for_ref());
-
-    let edit = TextEdit::insert(expr_node.syntax().text_range().start(), ampersands);
-    let source_change =
-        SourceChange::from_text_edit(d.expr.file_id.original_file(ctx.sema.db), edit);
-    acc.push(fix("add_reference_here", "Add reference here", source_change, range));
-    Some(())
-}
-
-fn add_missing_ok_or_some(
-    ctx: &DiagnosticsContext<'_>,
-    d: &hir::TypeMismatch,
-    acc: &mut Vec<Assist>,
-) -> Option<()> {
-    let root = ctx.sema.db.parse_or_expand(d.expr.file_id)?;
-    let tail_expr = d.expr.value.to_node(&root);
-    let tail_expr_range = tail_expr.syntax().text_range();
-    let scope = ctx.sema.scope(tail_expr.syntax());
-
-    let expected_adt = d.expected.as_adt()?;
-    let expected_enum = expected_adt.as_enum()?;
-
-    let famous_defs = FamousDefs(&ctx.sema, scope.krate());
-    let core_result = famous_defs.core_result_Result();
-    let core_option = famous_defs.core_option_Option();
-
-    if Some(expected_enum) != core_result && Some(expected_enum) != core_option {
-        return None;
-    }
-
-    let variant_name = if Some(expected_enum) == core_result { "Ok" } else { "Some" };
-
-    let wrapped_actual_ty = expected_adt.ty_with_args(ctx.sema.db, &[d.actual.clone()]);
-
-    if !d.expected.could_unify_with(ctx.sema.db, &wrapped_actual_ty) {
-        return None;
-    }
-
-    let mut builder = TextEdit::builder();
-    for_each_tail_expr(&tail_expr, &mut |expr| {
-        if ctx.sema.type_of_expr(expr).map(TypeInfo::adjusted).as_ref() != Some(&d.expected) {
-            builder.insert(expr.syntax().text_range().start(), format!("{}(", variant_name));
-            builder.insert(expr.syntax().text_range().end(), ")".to_string());
-        }
-    });
-    let source_change =
-        SourceChange::from_text_edit(d.expr.file_id.original_file(ctx.sema.db), builder.finish());
-    let name = format!("Wrap in {}", variant_name);
-    acc.push(fix("wrap_tail_expr", &name, source_change, tail_expr_range));
-    Some(())
-}
-
-fn remove_semicolon(
-    ctx: &DiagnosticsContext<'_>,
-    d: &hir::TypeMismatch,
-    acc: &mut Vec<Assist>,
-) -> Option<()> {
-    let root = ctx.sema.db.parse_or_expand(d.expr.file_id)?;
-    let expr = d.expr.value.to_node(&root);
-    if !d.actual.is_unit() {
-        return None;
-    }
-    let block = BlockExpr::cast(expr.syntax().clone())?;
-    let expr_before_semi =
-        block.statements().last().and_then(|s| ExprStmt::cast(s.syntax().clone()))?;
-    let type_before_semi = ctx.sema.type_of_expr(&expr_before_semi.expr()?)?.original();
-    if !type_before_semi.could_coerce_to(ctx.sema.db, &d.expected) {
-        return None;
-    }
-    let semicolon_range = expr_before_semi.semicolon_token()?.text_range();
-
-    let edit = TextEdit::delete(semicolon_range);
-    let source_change =
-        SourceChange::from_text_edit(d.expr.file_id.original_file(ctx.sema.db), edit);
-
-    acc.push(fix("remove_semicolon", "Remove this semicolon", source_change, semicolon_range));
-    Some(())
-}
-
-#[cfg(test)]
-mod tests {
-    use crate::tests::{check_diagnostics, check_fix, check_no_fix};
-
-    #[test]
-    fn missing_reference() {
-        check_diagnostics(
-            r#"
-fn main() {
-    test(123);
-       //^^^ 💡 error: expected &i32, found i32
-}
-fn test(arg: &i32) {}
-"#,
-        );
-    }
-
-    #[test]
-    fn test_add_reference_to_int() {
-        check_fix(
-            r#"
-fn main() {
-    test(123$0);
-}
-fn test(arg: &i32) {}
-            "#,
-            r#"
-fn main() {
-    test(&123);
-}
-fn test(arg: &i32) {}
-            "#,
-        );
-    }
-
-    #[test]
-    fn test_add_mutable_reference_to_int() {
-        check_fix(
-            r#"
-fn main() {
-    test($0123);
-}
-fn test(arg: &mut i32) {}
-            "#,
-            r#"
-fn main() {
-    test(&mut 123);
-}
-fn test(arg: &mut i32) {}
-            "#,
-        );
-    }
-
-    #[test]
-    fn test_add_reference_to_array() {
-        check_fix(
-            r#"
-//- minicore: coerce_unsized
-fn main() {
-    test($0[1, 2, 3]);
-}
-fn test(arg: &[i32]) {}
-            "#,
-            r#"
-fn main() {
-    test(&[1, 2, 3]);
-}
-fn test(arg: &[i32]) {}
-            "#,
-        );
-    }
-
-    #[test]
-    fn test_add_reference_with_autoderef() {
-        check_fix(
-            r#"
-//- minicore: coerce_unsized, deref
-struct Foo;
-struct Bar;
-impl core::ops::Deref for Foo {
-    type Target = Bar;
-}
-
-fn main() {
-    test($0Foo);
-}
-fn test(arg: &Bar) {}
-            "#,
-            r#"
-struct Foo;
-struct Bar;
-impl core::ops::Deref for Foo {
-    type Target = Bar;
-}
-
-fn main() {
-    test(&Foo);
-}
-fn test(arg: &Bar) {}
-            "#,
-        );
-    }
-
-    #[test]
-    fn test_add_reference_to_method_call() {
-        check_fix(
-            r#"
-fn main() {
-    Test.call_by_ref($0123);
-}
-struct Test;
-impl Test {
-    fn call_by_ref(&self, arg: &i32) {}
-}
-            "#,
-            r#"
-fn main() {
-    Test.call_by_ref(&123);
-}
-struct Test;
-impl Test {
-    fn call_by_ref(&self, arg: &i32) {}
-}
-            "#,
-        );
-    }
-
-    #[test]
-    fn test_add_reference_to_let_stmt() {
-        check_fix(
-            r#"
-fn main() {
-    let test: &i32 = $0123;
-}
-            "#,
-            r#"
-fn main() {
-    let test: &i32 = &123;
-}
-            "#,
-        );
-    }
-
-    #[test]
-    fn test_add_mutable_reference_to_let_stmt() {
-        check_fix(
-            r#"
-fn main() {
-    let test: &mut i32 = $0123;
-}
-            "#,
-            r#"
-fn main() {
-    let test: &mut i32 = &mut 123;
-}
-            "#,
-        );
-    }
-
-    #[test]
-    fn test_wrap_return_type_option() {
-        check_fix(
-            r#"
-//- minicore: option, result
-fn div(x: i32, y: i32) -> Option<i32> {
-    if y == 0 {
-        return None;
-    }
-    x / y$0
-}
-"#,
-            r#"
-fn div(x: i32, y: i32) -> Option<i32> {
-    if y == 0 {
-        return None;
-    }
-    Some(x / y)
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn test_wrap_return_type_option_tails() {
-        check_fix(
-            r#"
-//- minicore: option, result
-fn div(x: i32, y: i32) -> Option<i32> {
-    if y == 0 {
-        0
-    } else if true {
-        100
-    } else {
-        None
-    }$0
-}
-"#,
-            r#"
-fn div(x: i32, y: i32) -> Option<i32> {
-    if y == 0 {
-        Some(0)
-    } else if true {
-        Some(100)
-    } else {
-        None
-    }
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn test_wrap_return_type() {
-        check_fix(
-            r#"
-//- minicore: option, result
-fn div(x: i32, y: i32) -> Result<i32, ()> {
-    if y == 0 {
-        return Err(());
-    }
-    x / y$0
-}
-"#,
-            r#"
-fn div(x: i32, y: i32) -> Result<i32, ()> {
-    if y == 0 {
-        return Err(());
-    }
-    Ok(x / y)
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn test_wrap_return_type_handles_generic_functions() {
-        check_fix(
-            r#"
-//- minicore: option, result
-fn div<T>(x: T) -> Result<T, i32> {
-    if x == 0 {
-        return Err(7);
-    }
-    $0x
-}
-"#,
-            r#"
-fn div<T>(x: T) -> Result<T, i32> {
-    if x == 0 {
-        return Err(7);
-    }
-    Ok(x)
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn test_wrap_return_type_handles_type_aliases() {
-        check_fix(
-            r#"
-//- minicore: option, result
-type MyResult<T> = Result<T, ()>;
-
-fn div(x: i32, y: i32) -> MyResult<i32> {
-    if y == 0 {
-        return Err(());
-    }
-    x $0/ y
-}
-"#,
-            r#"
-type MyResult<T> = Result<T, ()>;
-
-fn div(x: i32, y: i32) -> MyResult<i32> {
-    if y == 0 {
-        return Err(());
-    }
-    Ok(x / y)
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn test_in_const_and_static() {
-        check_fix(
-            r#"
-//- minicore: option, result
-static A: Option<()> = {($0)};
-            "#,
-            r#"
-static A: Option<()> = {Some(())};
-            "#,
-        );
-        check_fix(
-            r#"
-//- minicore: option, result
-const _: Option<()> = {($0)};
-            "#,
-            r#"
-const _: Option<()> = {Some(())};
-            "#,
-        );
-    }
-
-    #[test]
-    fn test_wrap_return_type_not_applicable_when_expr_type_does_not_match_ok_type() {
-        check_no_fix(
-            r#"
-//- minicore: option, result
-fn foo() -> Result<(), i32> { 0$0 }
-"#,
-        );
-    }
-
-    #[test]
-    fn test_wrap_return_type_not_applicable_when_return_type_is_not_result_or_option() {
-        check_no_fix(
-            r#"
-//- minicore: option, result
-enum SomeOtherEnum { Ok(i32), Err(String) }
-
-fn foo() -> SomeOtherEnum { 0$0 }
-"#,
-        );
-    }
-
-    #[test]
-    fn remove_semicolon() {
-        check_fix(r#"fn f() -> i32 { 92$0; }"#, r#"fn f() -> i32 { 92 }"#);
-    }
-}
diff --git a/crates/ide_diagnostics/src/lib.rs b/crates/ide_diagnostics/src/lib.rs
index 13ac0a2450..86d76751ad 100644
--- a/crates/ide_diagnostics/src/lib.rs
+++ b/crates/ide_diagnostics/src/lib.rs
@@ -24,6 +24,7 @@
 //! don't yet have a great pattern for how to do them properly.
 
 mod handlers {
+    pub(crate) mod add_reference_here;
     pub(crate) mod break_outside_of_loop;
     pub(crate) mod inactive_code;
     pub(crate) mod incorrect_case;
@@ -33,10 +34,11 @@ mod handlers {
     pub(crate) mod mismatched_arg_count;
     pub(crate) mod missing_fields;
     pub(crate) mod missing_match_arms;
+    pub(crate) mod missing_ok_or_some_in_tail_expr;
     pub(crate) mod missing_unsafe;
     pub(crate) mod no_such_field;
+    pub(crate) mod remove_this_semicolon;
     pub(crate) mod replace_filter_map_next_with_find_map;
-    pub(crate) mod type_mismatch;
     pub(crate) mod unimplemented_builtin_macro;
     pub(crate) mod unresolved_extern_crate;
     pub(crate) mod unresolved_import;
@@ -189,6 +191,7 @@ pub fn diagnostics(
     for diag in diags {
         #[rustfmt::skip]
         let d = match diag {
+            AnyDiagnostic::AddReferenceHere(d) => handlers::add_reference_here::add_reference_here(&ctx, &d),
             AnyDiagnostic::BreakOutsideOfLoop(d) => handlers::break_outside_of_loop::break_outside_of_loop(&ctx, &d),
             AnyDiagnostic::IncorrectCase(d) => handlers::incorrect_case::incorrect_case(&ctx, &d),
             AnyDiagnostic::MacroError(d) => handlers::macro_error::macro_error(&ctx, &d),
@@ -196,10 +199,11 @@ pub fn diagnostics(
             AnyDiagnostic::MismatchedArgCount(d) => handlers::mismatched_arg_count::mismatched_arg_count(&ctx, &d),
             AnyDiagnostic::MissingFields(d) => handlers::missing_fields::missing_fields(&ctx, &d),
             AnyDiagnostic::MissingMatchArms(d) => handlers::missing_match_arms::missing_match_arms(&ctx, &d),
+            AnyDiagnostic::MissingOkOrSomeInTailExpr(d) => handlers::missing_ok_or_some_in_tail_expr::missing_ok_or_some_in_tail_expr(&ctx, &d),
             AnyDiagnostic::MissingUnsafe(d) => handlers::missing_unsafe::missing_unsafe(&ctx, &d),
             AnyDiagnostic::NoSuchField(d) => handlers::no_such_field::no_such_field(&ctx, &d),
+            AnyDiagnostic::RemoveThisSemicolon(d) => handlers::remove_this_semicolon::remove_this_semicolon(&ctx, &d),
             AnyDiagnostic::ReplaceFilterMapNextWithFindMap(d) => handlers::replace_filter_map_next_with_find_map::replace_filter_map_next_with_find_map(&ctx, &d),
-            AnyDiagnostic::TypeMismatch(d) => handlers::type_mismatch::type_mismatch(&ctx, &d),
             AnyDiagnostic::UnimplementedBuiltinMacro(d) => handlers::unimplemented_builtin_macro::unimplemented_builtin_macro(&ctx, &d),
             AnyDiagnostic::UnresolvedExternCrate(d) => handlers::unresolved_extern_crate::unresolved_extern_crate(&ctx, &d),
             AnyDiagnostic::UnresolvedImport(d) => handlers::unresolved_import::unresolved_import(&ctx, &d),
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index ab197c671d..f7df51e36a 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -259,7 +259,7 @@ config_data! {
         /// Whether to show inlay type hints for compiler inserted reborrows.
         inlayHints_reborrowHints: bool                      = "false",
         /// Whether to show inlay type hints for elided lifetimes in function signatures.
-        inlayHints_lifetimeElisionHints: LifetimeElisionDef = "\"never\"",
+        inlayHints_lifetimeElisionHints_enable: LifetimeElisionDef = "\"never\"",
         /// Whether to prefer using parameter names as the name for elided lifetime hints if possible.
         inlayHints_lifetimeElisionHints_useParameterNames: bool  = "false",
         /// Whether to hide inlay hints for constructors.
@@ -862,7 +862,7 @@ impl Config {
             parameter_hints: self.data.inlayHints_parameterHints,
             chaining_hints: self.data.inlayHints_chainingHints,
             closure_return_type_hints: self.data.inlayHints_closureReturnTypeHints,
-            lifetime_elision_hints: match self.data.inlayHints_lifetimeElisionHints {
+            lifetime_elision_hints: match self.data.inlayHints_lifetimeElisionHints_enable {
                 LifetimeElisionDef::Always => LifetimeElisionHints::Always,
                 LifetimeElisionDef::Never => LifetimeElisionHints::Never,
                 LifetimeElisionDef::SkipTrivial => LifetimeElisionHints::SkipTrivial,
diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc
index 61bd36202d..bf8d482432 100644
--- a/docs/user/generated_config.adoc
+++ b/docs/user/generated_config.adoc
@@ -383,7 +383,7 @@ Whether to show inlay type hints for return types of closures with blocks.
 --
 Whether to show inlay type hints for compiler inserted reborrows.
 --
-[[rust-analyzer.inlayHints.lifetimeElisionHints]]rust-analyzer.inlayHints.lifetimeElisionHints (default: `"never"`)::
+[[rust-analyzer.inlayHints.lifetimeElisionHints.enable]]rust-analyzer.inlayHints.lifetimeElisionHints.enable (default: `"never"`)::
 +
 --
 Whether to show inlay type hints for elided lifetimes in function signatures.
diff --git a/editors/code/package.json b/editors/code/package.json
index 911fa2bd76..bf242d6895 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -800,7 +800,7 @@
                     "default": false,
                     "type": "boolean"
                 },
-                "rust-analyzer.inlayHints.lifetimeElisionHints": {
+                "rust-analyzer.inlayHints.lifetimeElisionHints.enable": {
                     "markdownDescription": "Whether to show inlay type hints for elided lifetimes in function signatures.",
                     "default": "never",
                     "type": "string",


Test patch:
diff --git a/crates/hir_ty/src/tests/simple.rs b/crates/hir_ty/src/tests/simple.rs
index de27c294f6..0d050f7461 100644
--- a/crates/hir_ty/src/tests/simple.rs
+++ b/crates/hir_ty/src/tests/simple.rs
@@ -1,6 +1,6 @@
 use expect_test::expect;
 
-use super::{check_infer, check_no_mismatches, check_types};
+use super::{check_infer, check_types};
 
 #[test]
 fn infer_box() {
@@ -2624,21 +2624,3 @@ pub mod prelude {
     "#,
     );
 }
-
-#[test]
-fn legacy_const_generics() {
-    check_no_mismatches(
-        r#"
-#[rustc_legacy_const_generics(1, 3)]
-fn mixed<const N1: &'static str, const N2: bool>(
-    a: u8,
-    b: i8,
-) {}
-
-fn f() {
-    mixed(0, "", -1, true);
-    mixed::<"", true>(0, -1);
-}
-    "#,
-    );
-}