Repository: rust-analyzer/rust-analyzer
Pull request #11788 (2022-03-22 14:56:14)

internal: Improve `find_path` and extern prelude handling


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/find_path.rs b/crates/hir_def/src/find_path.rs
index e1e5ded52a..bb89b8cff4 100644
--- a/crates/hir_def/src/find_path.rs
+++ b/crates/hir_def/src/find_path.rs
@@ -18,8 +18,7 @@ use crate::{
 /// *from where* you're referring to the item, hence the `from` parameter.
 pub fn find_path(db: &dyn DefDatabase, item: ItemInNs, from: ModuleId) -> Option<ModPath> {
     let _p = profile::span("find_path");
-    let mut visited_modules = FxHashSet::default();
-    find_path_inner(db, item, from, MAX_PATH_LEN, None, &mut visited_modules)
+    find_path_inner(db, item, from, None)
 }
 
 pub fn find_path_prefixed(
@@ -29,8 +28,7 @@ pub fn find_path_prefixed(
     prefix_kind: PrefixKind,
 ) -> Option<ModPath> {
     let _p = profile::span("find_path_prefixed");
-    let mut visited_modules = FxHashSet::default();
-    find_path_inner(db, item, from, MAX_PATH_LEN, Some(prefix_kind), &mut visited_modules)
+    find_path_inner(db, item, from, Some(prefix_kind))
 }
 
 const MAX_PATH_LEN: usize = 15;
@@ -56,12 +54,12 @@ impl ModPathExt for ModPath {
 fn check_self_super(def_map: &DefMap, item: ItemInNs, from: ModuleId) -> Option<ModPath> {
     if item == ItemInNs::Types(from.into()) {
         // - if the item is the module we're in, use `self`
-        Some(ModPath::from_segments(PathKind::Super(0), Vec::new()))
+        Some(ModPath::from_segments(PathKind::Super(0), None))
     } else if let Some(parent_id) = def_map[from.local_id].parent {
         // - if the item is the parent module, use `super` (this is not used recursively, since `super::super` is ugly)
         let parent_id = def_map.module_id(parent_id);
         if item == ItemInNs::Types(ModuleDefId::ModuleId(parent_id)) {
-            Some(ModPath::from_segments(PathKind::Super(1), Vec::new()))
+            Some(ModPath::from_segments(PathKind::Super(1), None))
         } else {
             None
         }
@@ -97,12 +95,25 @@ impl PrefixKind {
         self == &PrefixKind::ByCrate
     }
 }
-
 /// Attempts to find a path to refer to the given `item` visible from the `from` ModuleId
 fn find_path_inner(
     db: &dyn DefDatabase,
     item: ItemInNs,
     from: ModuleId,
+    prefixed: Option<PrefixKind>,
+) -> Option<ModPath> {
+    // FIXME: Do fast path for std/core libs?
+
+    let mut visited_modules = FxHashSet::default();
+    let def_map = from.def_map(db);
+    find_path_inner_(db, &def_map, from, item, MAX_PATH_LEN, prefixed, &mut visited_modules)
+}
+
+fn find_path_inner_(
+    db: &dyn DefDatabase,
+    def_map: &DefMap,
+    from: ModuleId,
+    item: ItemInNs,
     max_len: usize,
     mut prefixed: Option<PrefixKind>,
     visited_modules: &mut FxHashSet<ModuleId>,
@@ -114,19 +125,24 @@ fn find_path_inner(
     // Base cases:
 
     // - if the item is already in scope, return the name under which it is
-    let def_map = from.def_map(db);
     let scope_name = def_map.with_ancestor_maps(db, from.local_id, &mut |def_map, local_id| {
         def_map[local_id].scope.name_of(item).map(|(name, _)| name.clone())
     });
-    if prefixed.is_none() && scope_name.is_some() {
-        return scope_name
-            .map(|scope_name| ModPath::from_segments(PathKind::Plain, vec![scope_name]));
+    if prefixed.is_none() {
+        if let Some(scope_name) = scope_name {
+            return Some(ModPath::from_segments(PathKind::Plain, Some(scope_name)));
+        }
+    }
+
+    // - if the item is a builtin, it's in scope
+    if let ItemInNs::Types(ModuleDefId::BuiltinType(builtin)) = item {
+        return Some(ModPath::from_segments(PathKind::Plain, Some(builtin.as_name())));
     }
 
     // - if the item is the crate root, return `crate`
-    let root = def_map.crate_root(db);
-    if item == ItemInNs::Types(ModuleDefId::ModuleId(root)) {
-        return Some(ModPath::from_segments(PathKind::Crate, Vec::new()));
+    let crate_root = def_map.crate_root(db);
+    if item == ItemInNs::Types(ModuleDefId::ModuleId(crate_root)) {
+        return Some(ModPath::from_segments(PathKind::Crate, None));
     }
 
     if prefixed.filter(PrefixKind::is_absolute).is_none() {
@@ -136,25 +152,28 @@ fn find_path_inner(
     }
 
     // - if the item is the crate root of a dependency crate, return the name from the extern prelude
-    let root_def_map = root.def_map(db);
-    for (name, def_id) in root_def_map.extern_prelude() {
-        if item == ItemInNs::Types(*def_id) {
-            let name = scope_name.unwrap_or_else(|| name.clone());
-
-            let name_already_occupied_in_type_ns = def_map
-                .with_ancestor_maps(db, from.local_id, &mut |def_map, local_id| {
-                    def_map[local_id].scope.get(&name).take_types().filter(|&id| id != *def_id)
-                })
-                .is_some();
-            return Some(ModPath::from_segments(
-                if name_already_occupied_in_type_ns {
+    let root_def_map = crate_root.def_map(db);
+    if let ItemInNs::Types(ModuleDefId::ModuleId(item)) = item {
+        for (name, &def_id) in root_def_map.extern_prelude() {
+            if item == def_id {
+                let name = scope_name.unwrap_or_else(|| name.clone());
+
+                let name_already_occupied_in_type_ns = def_map
+                    .with_ancestor_maps(db, from.local_id, &mut |def_map, local_id| {
+                        def_map[local_id]
+                            .scope
+                            .type_(&name)
+                            .filter(|&(id, _)| id != ModuleDefId::ModuleId(def_id))
+                    })
+                    .is_some();
+                let kind = if name_already_occupied_in_type_ns {
                     cov_mark::hit!(ambiguous_crate_start);
                     PathKind::Abs
                 } else {
                     PathKind::Plain
-                },
-                vec![name],
-            ));
+                };
+                return Some(ModPath::from_segments(kind, Some(name)));
+            }
         }
     }
 
@@ -162,20 +181,14 @@ fn find_path_inner(
     if let Some(prelude_module) = root_def_map.prelude() {
         // Preludes in block DefMaps are ignored, only the crate DefMap is searched
         let prelude_def_map = prelude_module.def_map(db);
-        let prelude_scope: &crate::item_scope::ItemScope =
-            &prelude_def_map[prelude_module.local_id].scope;
+        let prelude_scope = &prelude_def_map[prelude_module.local_id].scope;
         if let Some((name, vis)) = prelude_scope.name_of(item) {
             if vis.is_visible_from(db, from) {
-                return Some(ModPath::from_segments(PathKind::Plain, vec![name.clone()]));
+                return Some(ModPath::from_segments(PathKind::Plain, Some(name.clone())));
             }
         }
     }
 
-    // - if the item is a builtin, it's in scope
-    if let ItemInNs::Types(ModuleDefId::BuiltinType(builtin)) = item {
-        return Some(ModPath::from_segments(PathKind::Plain, vec![builtin.as_name()]));
-    }
-
     // Recursive case:
     // - if the item is an enum variant, refer to it via the enum
     if let Some(ModuleDefId::EnumVariantId(variant)) = item.as_module_def_id() {
@@ -190,25 +203,24 @@ fn find_path_inner(
     }
 
     // - otherwise, look for modules containing (reexporting) it and import it from one of those
-
-    let crate_root = def_map.crate_root(db);
-    let crate_attrs = db.attrs(crate_root.into());
-    let prefer_no_std = crate_attrs.by_key("no_std").exists();
+    let prefer_no_std = db.attrs(crate_root.into()).by_key("no_std").exists();
     let mut best_path = None;
     let mut best_path_len = max_len;
 
     if item.krate(db) == Some(from.krate) {
         // Item was defined in the same crate that wants to import it. It cannot be found in any
         // dependency in this case.
+        // FIXME: this should have a fast path that doesn't look through the prelude again?
         for (module_id, name) in find_local_import_locations(db, item, from) {
             if !visited_modules.insert(module_id) {
                 cov_mark::hit!(recursive_imports);
                 continue;
             }
-            if let Some(mut path) = find_path_inner(
+            if let Some(mut path) = find_path_inner_(
                 db,
-                ItemInNs::Types(ModuleDefId::ModuleId(module_id)),
+                def_map,
                 from,
+                ItemInNs::Types(ModuleDefId::ModuleId(module_id)),
                 best_path_len - 1,
                 prefixed,
                 visited_modules,
@@ -233,16 +245,18 @@ fn find_path_inner(
             let import_map = db.import_map(dep.crate_id);
             import_map.import_info_for(item).and_then(|info| {
                 // Determine best path for containing module and append last segment from `info`.
-                let mut path = find_path_inner(
+                // FIXME: we should guide this to look up the path locally, or from the same crate again?
+                let mut path = find_path_inner_(
                     db,
-                    ItemInNs::Types(ModuleDefId::ModuleId(info.container)),
+                    def_map,
                     from,
+                    ItemInNs::Types(ModuleDefId::ModuleId(info.container)),
                     best_path_len - 1,
                     prefixed,
                     visited_modules,
                 )?;
                 cov_mark::hit!(partially_imported);
-                path.push_segment(info.path.segments.last().unwrap().clone());
+                path.push_segment(info.path.segments.last()?.clone());
                 Some(path)
             })
         });
@@ -267,7 +281,7 @@ fn find_path_inner(
 
     match prefixed.map(PrefixKind::prefix) {
         Some(prefix) => best_path.or_else(|| {
-            scope_name.map(|scope_name| ModPath::from_segments(prefix, vec![scope_name]))
+            scope_name.map(|scope_name| ModPath::from_segments(prefix, Some(scope_name)))
         }),
         None => best_path,
     }
@@ -370,8 +384,8 @@ fn find_local_import_locations(
         }
 
         // Descend into all modules visible from `from`.
-        for (_, per_ns) in data.scope.entries() {
-            if let Some((ModuleDefId::ModuleId(module), vis)) = per_ns.take_types_vis() {
+        for (ty, vis) in data.scope.types() {
+            if let ModuleDefId::ModuleId(module) = ty {
                 if vis.is_visible_from(db, from) {
                     worklist.push(module);
                 }
@@ -415,15 +429,7 @@ mod tests {
             .take_types()
             .unwrap();
 
-        let mut visited_modules = FxHashSet::default();
-        let found_path = find_path_inner(
-            &db,
-            ItemInNs::Types(resolved),
-            module,
-            MAX_PATH_LEN,
-            prefix_kind,
-            &mut visited_modules,
-        );
+        let found_path = find_path_inner(&db, ItemInNs::Types(resolved), module, prefix_kind);
         assert_eq!(found_path, Some(mod_path), "{:?}", prefix_kind);
     }
 
diff --git a/crates/hir_def/src/item_scope.rs b/crates/hir_def/src/item_scope.rs
index f1330b34d1..0096df2288 100644
--- a/crates/hir_def/src/item_scope.rs
+++ b/crates/hir_def/src/item_scope.rs
@@ -119,6 +119,12 @@ impl ItemScope {
         self.values.values().copied()
     }
 
+    pub fn types(
+        &self,
+    ) -> impl Iterator<Item = (ModuleDefId, Visibility)> + ExactSizeIterator + '_ {
+        self.types.values().copied()
+    }
+
     pub fn unnamed_consts(&self) -> impl Iterator<Item = ConstId> + '_ {
         self.unnamed_consts.iter().copied()
     }
@@ -142,6 +148,10 @@ impl ItemScope {
         }
     }
 
+    pub(crate) fn type_(&self, name: &Name) -> Option<(ModuleDefId, Visibility)> {
+        self.types.get(name).copied()
+    }
+
     /// XXX: this is O(N) rather than O(1), try to not introduce new usages.
     pub(crate) fn name_of(&self, item: ItemInNs) -> Option<(&Name, Visibility)> {
         let (def, mut iter) = match item {
diff --git a/crates/hir_def/src/nameres.rs b/crates/hir_def/src/nameres.rs
index e9d3d976f9..e923d883ec 100644
--- a/crates/hir_def/src/nameres.rs
+++ b/crates/hir_def/src/nameres.rs
@@ -75,7 +75,7 @@ use crate::{
     path::ModPath,
     per_ns::PerNs,
     visibility::Visibility,
-    AstId, BlockId, BlockLoc, FunctionId, LocalModuleId, ModuleDefId, ModuleId, ProcMacroId,
+    AstId, BlockId, BlockLoc, FunctionId, LocalModuleId, ModuleId, ProcMacroId,
 };
 
 /// Contains the results of (early) name resolution.
@@ -98,7 +98,7 @@ pub struct DefMap {
     /// marked with the `prelude_import` attribute, or (in the normal case) from
     /// a dependency (`std` or `core`).
     prelude: Option<ModuleId>,
-    extern_prelude: FxHashMap<Name, ModuleDefId>,
+    extern_prelude: FxHashMap<Name, ModuleId>,
 
     /// Side table for resolving derive helpers.
     exported_derives: FxHashMap<MacroDefId, Box<[Name]>>,
@@ -318,7 +318,7 @@ impl DefMap {
         self.prelude
     }
 
-    pub(crate) fn extern_prelude(&self) -> impl Iterator<Item = (&Name, &ModuleDefId)> + '_ {
+    pub(crate) fn extern_prelude(&self) -> impl Iterator<Item = (&Name, &ModuleId)> + '_ {
         self.extern_prelude.iter()
     }
 
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs
index 8e6b5283f1..ea29f80437 100644
--- a/crates/hir_def/src/nameres/collector.rs
+++ b/crates/hir_def/src/nameres/collector.rs
@@ -70,7 +70,7 @@ pub(super) fn collect_defs(db: &dyn DefDatabase, mut def_map: DefMap, tree_id: T
         deps.insert(dep.as_name(), dep_root.into());
 
         if dep.is_prelude() && !tree_id.is_block() {
-            def_map.extern_prelude.insert(dep.as_name(), dep_root.into());
+            def_map.extern_prelude.insert(dep.as_name(), dep_root);
         }
     }
 
@@ -234,7 +234,7 @@ enum MacroDirectiveKind {
 struct DefCollector<'a> {
     db: &'a dyn DefDatabase,
     def_map: DefMap,
-    deps: FxHashMap<Name, ModuleDefId>,
+    deps: FxHashMap<Name, ModuleId>,
     glob_imports: FxHashMap<LocalModuleId, Vec<(LocalModuleId, Visibility)>>,
     unresolved_imports: Vec<ImportDirective>,
     resolved_imports: Vec<ImportDirective>,
@@ -687,9 +687,7 @@ impl DefCollector<'_> {
             self.def_map.edition,
         );
 
-        let res = self.resolve_extern_crate(&extern_crate.name);
-
-        if let Some(ModuleDefId::ModuleId(m)) = res.take_types() {
+        if let Some(m) = self.resolve_extern_crate(&extern_crate.name) {
             if m == self.def_map.module_id(current_module_id) {
                 cov_mark::hit!(ignore_macro_use_extern_crate_self);
                 return;
@@ -757,10 +755,11 @@ impl DefCollector<'_> {
 
             let res = self.resolve_extern_crate(name);
 
-            if res.is_none() {
-                PartialResolvedImport::Unresolved
-            } else {
-                PartialResolvedImport::Resolved(res)
+            match res {
+                Some(res) => {
+                    PartialResolvedImport::Resolved(PerNs::types(res.into(), Visibility::Public))
+                }
+                None => PartialResolvedImport::Unresolved,
             }
         } else {
             let res = self.def_map.resolve_path_fp_with_macro(
@@ -796,7 +795,7 @@ impl DefCollector<'_> {
         }
     }
 
-    fn resolve_extern_crate(&self, name: &Name) -> PerNs {
+    fn resolve_extern_crate(&self, name: &Name) -> Option<ModuleId> {
         if *name == name!(self) {
             cov_mark::hit!(extern_crate_self_as);
             let root = match self.def_map.block {
@@ -806,9 +805,9 @@ impl DefCollector<'_> {
                 }
                 None => self.def_map.module_id(self.def_map.root()),
             };
-            PerNs::types(root.into(), Visibility::Public)
+            Some(root)
         } else {
-            self.deps.get(name).map_or(PerNs::none(), |&it| PerNs::types(it, Visibility::Public))
+            self.deps.get(name).copied()
         }
     }
 
@@ -846,7 +845,8 @@ impl DefCollector<'_> {
 
                 // extern crates in the crate root are special-cased to insert entries into the extern prelude: rust-lang/rust#54658
                 if import.is_extern_crate && module_id == self.def_map.root {
-                    if let (Some(def), Some(name)) = (def.take_types(), name) {
+                    if let (Some(ModuleDefId::ModuleId(def)), Some(name)) = (def.take_types(), name)
+                    {
                         self.def_map.extern_prelude.insert(name.clone(), def);
                     }
                 }
diff --git a/crates/hir_def/src/nameres/path_resolution.rs b/crates/hir_def/src/nameres/path_resolution.rs
index 809c6e0289..cc5fc0a52a 100644
--- a/crates/hir_def/src/nameres/path_resolution.rs
+++ b/crates/hir_def/src/nameres/path_resolution.rs
@@ -20,7 +20,7 @@ use crate::{
     path::{ModPath, PathKind},
     per_ns::PerNs,
     visibility::{RawVisibility, Visibility},
-    AdtId, CrateId, EnumVariantId, LocalModuleId, ModuleDefId,
+    AdtId, CrateId, EnumVariantId, LocalModuleId, ModuleDefId, ModuleId,
 };
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -63,19 +63,11 @@ impl DefMap {
         &self,
         db: &dyn DefDatabase,
         name: &Name,
-    ) -> PerNs {
-        let arc;
-        let root = match self.block {
-            Some(_) => {
-                arc = self.crate_root(db).def_map(db);
-                &*arc
-            }
-            None => self,
-        };
-
-        root.extern_prelude
-            .get(name)
-            .map_or(PerNs::none(), |&it| PerNs::types(it, Visibility::Public))
+    ) -> Option<ModuleId> {
+        match self.block {
+            Some(_) => self.crate_root(db).def_map(db).extern_prelude.get(name).copied(),
+            None => self.extern_prelude.get(name).copied(),
+        }
     }
 
     pub(crate) fn resolve_visibility(
@@ -273,9 +265,9 @@ impl DefMap {
                     Some((_, segment)) => segment,
                     None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
                 };
-                if let Some(def) = self.extern_prelude.get(segment) {
+                if let Some(&def) = self.extern_prelude.get(segment) {
                     tracing::debug!("absolute path {:?} resolved to crate {:?}", path, def);
-                    PerNs::types(*def, Visibility::Public)
+                    PerNs::types(def.into(), Visibility::Public)
                 } else {
                     return ResolvePathResult::empty(ReachedFixedPoint::No); // extern crate declarations can add to the extern prelude
                 }
@@ -408,7 +400,7 @@ impl DefMap {
         let from_extern_prelude = self
             .extern_prelude
             .get(name)
-            .map_or(PerNs::none(), |&it| PerNs::types(it, Visibility::Public));
+            .map_or(PerNs::none(), |&it| PerNs::types(it.into(), Visibility::Public));
 
         let from_prelude = self.resolve_in_prelude(db, name);
 
@@ -429,7 +421,9 @@ impl DefMap {
             None => self,
         };
         let from_crate_root = crate_def_map[crate_def_map.root].scope.get(name);
-        let from_extern_prelude = self.resolve_name_in_extern_prelude(db, name);
+        let from_extern_prelude = self
+            .resolve_name_in_extern_prelude(db, name)
+            .map_or(PerNs::none(), |it| PerNs::types(it.into(), Visibility::Public));
 
         from_crate_root.or(from_extern_prelude)
     }
diff --git a/crates/hir_def/src/resolver.rs b/crates/hir_def/src/resolver.rs
index b8c88282d6..ab99627d18 100644
--- a/crates/hir_def/src/resolver.rs
+++ b/crates/hir_def/src/resolver.rs
@@ -498,7 +498,7 @@ impl Scope {
                     acc.add(name, ScopeDef::ModuleDef(ModuleDefId::MacroId(MacroId::from(mac))));
                 });
                 m.def_map.extern_prelude().for_each(|(name, &def)| {
-                    acc.add(name, ScopeDef::ModuleDef(def));
+                    acc.add(name, ScopeDef::ModuleDef(ModuleDefId::ModuleId(def)));
                 });
                 BUILTIN_SCOPE.iter().for_each(|(name, &def)| {
                     acc.add_per_ns(name, def);
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_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),


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);
-}
-    "#,
-    );
-}