Repository: rust-analyzer/rust-analyzer
Pull request #11755 (2022-03-18 17:11:41)

feat: Implement lifetime elision hints
With names on:
![Code_erl26zKvuf](https://user-images.githubusercontent.com/3757771/159134856-e2c75d2d-f17c-45c7-9a78-3da5ee8b1acd.png)
With names off:
![Code_MRP1Pbfe9d](https://user-images.githubusercontent.com/3757771/159134857-30fac3a1-825e-4f49-ba9b-9fa0bb215694.png)



Patch:
diff --git a/Cargo.lock b/Cargo.lock
index 5351da334a..fab0edfe30 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -37,9 +37,9 @@ dependencies = [
 
 [[package]]
 name = "anyhow"
-version = "1.0.56"
+version = "1.0.53"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4361135be9122e0870de935d7c439aef945b9f9ddd4199a553b5270b49c82a27"
+checksum = "94a45b455c14666b85fc40a019e8ab9eb75e3a124e05494f5397122bc9eb06e0"
 
 [[package]]
 name = "anymap"
@@ -132,9 +132,9 @@ dependencies = [
 
 [[package]]
 name = "cargo_metadata"
-version = "0.14.2"
+version = "0.14.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4acbb09d9ee8e23699b9634375c72795d095bf268439da88562cf9b501f181fa"
+checksum = "ba2ae6de944143141f6155a473a6b02f66c7c3f9f47316f802f80204ebfe6e12"
 dependencies = [
  "camino",
  "cargo-platform",
@@ -145,9 +145,9 @@ dependencies = [
 
 [[package]]
 name = "cc"
-version = "1.0.73"
+version = "1.0.72"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
+checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee"
 
 [[package]]
 name = "cfg"
@@ -223,11 +223,11 @@ dependencies = [
 
 [[package]]
 name = "countme"
-version = "3.0.1"
+version = "3.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7704b5fdd17b18ae31c4c1da5a2e0305a2bf17b5249300a9ee9ed7b72114c636"
+checksum = "03746e0c6dd9b5d2d9132ffe0bede35fb5f815604fd371bb42599fd37bc8e483"
 dependencies = [
- "dashmap",
+ "dashmap 4.0.2",
  "once_cell",
  "rustc-hash",
 ]
@@ -249,9 +249,9 @@ dependencies = [
 
 [[package]]
 name = "crossbeam-channel"
-version = "0.5.3"
+version = "0.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fdbfe11fe19ff083c48923cf179540e8cd0535903dc35e178a1fdeeb59aef51f"
+checksum = "e54ea8bc3fb1ee042f5aace6e3c6e025d3874866da222930f70ce62aceba0bfa"
 dependencies = [
  "cfg-if",
  "crossbeam-utils",
@@ -270,11 +270,10 @@ dependencies = [
 
 [[package]]
 name = "crossbeam-epoch"
-version = "0.9.8"
+version = "0.9.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1145cf131a2c6ba0615079ab6a638f7e1973ac9c2634fcbeaaad6114246efe8c"
+checksum = "c00d6d2ea26e8b151d99093005cb442fb9a37aeaca582a03ec70946f49ab5ed9"
 dependencies = [
- "autocfg",
  "cfg-if",
  "crossbeam-utils",
  "lazy_static",
@@ -284,9 +283,9 @@ dependencies = [
 
 [[package]]
 name = "crossbeam-utils"
-version = "0.8.8"
+version = "0.8.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38"
+checksum = "b5e5bed1f1c269533fa816a0a5492b3545209a205ca1a54842be180eb63a16a6"
 dependencies = [
  "cfg-if",
  "lazy_static",
@@ -294,9 +293,19 @@ dependencies = [
 
 [[package]]
 name = "dashmap"
-version = "5.2.0"
+version = "4.0.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c8858831f7781322e539ea39e72449c46b059638250c14344fec8d0aa6e539c"
+checksum = "e77a43b28d0668df09411cb0bc9a8c2adc40f9a048afe863e05fd43251e8e39c"
+dependencies = [
+ "cfg-if",
+ "num_cpus",
+]
+
+[[package]]
+name = "dashmap"
+version = "5.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c0834a35a3fce649144119e18da2a4d8ed12ef3862f47183fd46f625d072d96c"
 dependencies = [
  "cfg-if",
  "num_cpus",
@@ -499,7 +508,7 @@ dependencies = [
  "base_db",
  "cfg",
  "cov-mark",
- "dashmap",
+ "dashmap 5.1.0",
  "drop_bomb",
  "either",
  "expect-test",
@@ -821,9 +830,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
 
 [[package]]
 name = "libc"
-version = "0.2.120"
+version = "0.2.117"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ad5c14e80759d0939d013e6ca49930e59fc53dd8e5009132f76240c179380c09"
+checksum = "e74d72e0f9b65b5b4ca49a346af3976df0f9c61d550727f349ecd559f251a26c"
 
 [[package]]
 name = "libloading"
@@ -837,9 +846,9 @@ dependencies = [
 
 [[package]]
 name = "libmimalloc-sys"
-version = "0.1.24"
+version = "0.1.23"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7705fc40f6ed493f73584abbb324e74f96b358ff60dfe5659a0f8fc12c590a69"
+checksum = "9636c194f9db483f4d0adf2f99a65011a99f904bd222bbd67fb4df4f37863c30"
 dependencies = [
  "cc",
 ]
@@ -947,9 +956,9 @@ dependencies = [
 
 [[package]]
 name = "mimalloc"
-version = "0.1.28"
+version = "0.1.27"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b0dfa131390c2f6bdb3242f65ff271fcdaca5ff7b6c08f28398be7f2280e3926"
+checksum = "cf5f78c1d9892fb5677a8b2f543f967ab891ac0f71feecd961435b74f877283a"
 dependencies = [
  "libmimalloc-sys",
 ]
@@ -966,15 +975,14 @@ dependencies = [
 
 [[package]]
 name = "mio"
-version = "0.8.1"
+version = "0.7.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7ba42135c6a5917b9db9cd7b293e5409e1c6b041e6f9825e92e55a894c63b6f8"
+checksum = "8067b404fe97c70829f082dec8bcf4f71225d7eaea1d8645349cb76fa06205cc"
 dependencies = [
  "libc",
  "log",
  "miow 0.3.7",
  "ntapi",
- "wasi",
  "winapi",
 ]
 
@@ -998,9 +1006,9 @@ dependencies = [
 
 [[package]]
 name = "notify"
-version = "5.0.0-pre.14"
+version = "5.0.0-pre.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d13c22db70a63592e098fb51735bab36646821e6389a0ba171f3549facdf0b74"
+checksum = "245d358380e2352c2d020e8ee62baac09b3420f1f6c012a31326cfced4ad487d"
 dependencies = [
  "bitflags",
  "crossbeam-channel",
@@ -1053,9 +1061,9 @@ dependencies = [
 
 [[package]]
 name = "once_cell"
-version = "1.10.0"
+version = "1.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9"
+checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5"
 
 [[package]]
 name = "oorandom"
@@ -1274,9 +1282,9 @@ dependencies = [
 
 [[package]]
 name = "pulldown-cmark-to-cmark"
-version = "10.0.1"
+version = "10.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3eafb76310f7dc895b5d83d24f2a00e244fc8a68ef94f4257eb4060984d0385f"
+checksum = "d1dd19382cbbcee6ce498f65d5acaa6016e41ec701c9b0aef29a689bd7062f88"
 dependencies = [
  "pulldown-cmark",
 ]
@@ -1317,18 +1325,18 @@ dependencies = [
 
 [[package]]
 name = "redox_syscall"
-version = "0.2.11"
+version = "0.2.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8380fe0152551244f0747b1bf41737e0f8a74f97a14ccefd1148187271634f3c"
+checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff"
 dependencies = [
  "bitflags",
 ]
 
 [[package]]
 name = "regex"
-version = "1.5.5"
+version = "1.5.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286"
+checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
 dependencies = [
  "regex-syntax",
 ]
@@ -1350,9 +1358,9 @@ checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
 
 [[package]]
 name = "rowan"
-version = "0.15.4"
+version = "0.15.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a4c1112d7b23c800be3a0dae244886b71d96b1461b57b31b56e4c679acbe614f"
+checksum = "c72a0c863fef62cfd9ca7b72a35b422d7672dbffac4ac8003dcc3be7837d871d"
 dependencies = [
  "countme",
  "hashbrown 0.12.0",
@@ -1493,9 +1501,9 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
 
 [[package]]
 name = "semver"
-version = "1.0.6"
+version = "1.0.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a4a3381e03edd24287172047536f20cabde766e2cd3e65e6b00fb3af51c4f38d"
+checksum = "0486718e92ec9a68fbed73bb5ef687d71103b142595b406835649bebd33f72c7"
 dependencies = [
  "serde",
 ]
@@ -1593,9 +1601,9 @@ dependencies = [
 
 [[package]]
 name = "syn"
-version = "1.0.89"
+version = "1.0.86"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ea297be220d52398dcc07ce15a209fce436d361735ac1db700cab3b6cdfb9f54"
+checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1695,9 +1703,9 @@ dependencies = [
 
 [[package]]
 name = "tikv-jemalloc-sys"
-version = "0.4.3+5.2.1-patched.2"
+version = "0.4.2+5.2.1-patched.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a1792ccb507d955b46af42c123ea8863668fae24d03721e40cad6a41773dbb49"
+checksum = "5844e429d797c62945a566f8da4e24c7fe3fbd5d6617fd8bf7a0b7dc1ee0f22e"
 dependencies = [
  "cc",
  "fs_extra",
@@ -1706,9 +1714,9 @@ dependencies = [
 
 [[package]]
 name = "tikv-jemallocator"
-version = "0.4.3"
+version = "0.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a5b7bcecfafe4998587d636f9ae9d55eb9d0499877b88757767c346875067098"
+checksum = "3c14a5a604eb8715bc5785018a37d00739b180bcf609916ddf4393d33d49ccdf"
 dependencies = [
  "libc",
  "tikv-jemalloc-sys",
@@ -1738,9 +1746,9 @@ dependencies = [
 
 [[package]]
 name = "tracing"
-version = "0.1.32"
+version = "0.1.30"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4a1bdf54a7c28a2bbf701e1d2233f6c77f473486b94bee4f9678da5a148dca7f"
+checksum = "2d8d93354fe2a8e50d5953f5ae2e47a3fc2ef03292e7ea46e3cc38f549525fb9"
 dependencies = [
  "cfg-if",
  "pin-project-lite",
@@ -1750,9 +1758,9 @@ dependencies = [
 
 [[package]]
 name = "tracing-attributes"
-version = "0.1.20"
+version = "0.1.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2e65ce065b4b5c53e73bb28912318cb8c9e9ad3921f1d669eb0e68b4c8143a2b"
+checksum = "8276d9a4a3a558d7b7ad5303ad50b53d58264641b82914b7ada36bd762e7a716"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1761,9 +1769,9 @@ dependencies = [
 
 [[package]]
 name = "tracing-core"
-version = "0.1.23"
+version = "0.1.22"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aa31669fa42c09c34d94d8165dd2012e8ff3c66aca50f3bb226b68f216f2706c"
+checksum = "03cfcb51380632a72d3111cb8d3447a8d908e577d31beeac006f836383d29a23"
 dependencies = [
  "lazy_static",
  "valuable",
@@ -1782,9 +1790,9 @@ dependencies = [
 
 [[package]]
 name = "tracing-subscriber"
-version = "0.3.9"
+version = "0.3.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9e0ab7bdc962035a87fba73f3acca9b8a8d0034c2e6f60b84aeaaddddc155dce"
+checksum = "74786ce43333fcf51efe947aed9718fbe46d5c7328ec3f1029e818083966d9aa"
 dependencies = [
  "lazy_static",
  "matchers",
@@ -1924,12 +1932,6 @@ dependencies = [
  "winapi-util",
 ]
 
-[[package]]
-name = "wasi"
-version = "0.11.0+wasi-snapshot-preview1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
-
 [[package]]
 name = "winapi"
 version = "0.3.9"
@@ -2055,33 +2057,33 @@ checksum = "06069a848f95fceae3e5e03c0ddc8cb78452b56654ee0c8e68f938cf790fb9e3"
 
 [[package]]
 name = "xflags"
-version = "0.2.4"
+version = "0.2.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3f14fe1ed41a5a2b5ef3f565586c4a8a559ee55d3953faab360a771135bdee00"
+checksum = "2c2854f07e689d7388420d1c73512073630df31ec5ddf18554f64aea0ae73775"
 dependencies = [
  "xflags-macros",
 ]
 
 [[package]]
 name = "xflags-macros"
-version = "0.2.4"
+version = "0.2.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "45d11d5fc2a97287eded8b170ca80533b3c42646dd7fa386a5eb045817921022"
+checksum = "da260301476ad19a4733a0e930db8227a48ea04561e235a5102978145ec69fcc"
 
 [[package]]
 name = "xshell"
-version = "0.2.1"
+version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4884417669886d3abff14feec797179526ade713f212e54ec08b19bc6bdc86aa"
+checksum = "3332cab90be2998a2aacb6494db45344bd16dfcc43ff36c42255018c6bcc96be"
 dependencies = [
  "xshell-macros",
 ]
 
 [[package]]
 name = "xshell-macros"
-version = "0.2.1"
+version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "37d92065701c3611323f96eac5475b995421fc7eb2bcba1336cdd80b9b2fb68f"
+checksum = "f47e54cffa76000b7641328ab3bb1e146f93a1690ab86c5909c656f49d91019c"
 
 [[package]]
 name = "xtask"
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 56832d6f8b..9207614c68 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -1547,7 +1547,9 @@ impl SelfParam {
 
 impl HasVisibility for Function {
     fn visibility(&self, db: &dyn HirDatabase) -> Visibility {
-        db.function_visibility(self.id)
+        let function_data = db.function_data(self.id);
+        let visibility = &function_data.visibility;
+        visibility.resolve(db.upcast(), &self.id.resolver(db.upcast()))
     }
 }
 
@@ -1598,7 +1600,9 @@ impl Const {
 
 impl HasVisibility for Const {
     fn visibility(&self, db: &dyn HirDatabase) -> Visibility {
-        db.const_visibility(self.id)
+        let function_data = db.const_data(self.id);
+        let visibility = &function_data.visibility;
+        visibility.resolve(db.upcast(), &self.id.resolver(db.upcast()))
     }
 }
 
diff --git a/crates/hir_def/src/db.rs b/crates/hir_def/src/db.rs
index 74bb7472d5..934d13c067 100644
--- a/crates/hir_def/src/db.rs
+++ b/crates/hir_def/src/db.rs
@@ -175,13 +175,9 @@ pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> {
     #[salsa::invoke(visibility::field_visibilities_query)]
     fn field_visibilities(&self, var: VariantId) -> Arc<ArenaMap<LocalFieldId, Visibility>>;
 
-    // FIXME: unify function_visibility and const_visibility?
     #[salsa::invoke(visibility::function_visibility_query)]
     fn function_visibility(&self, def: FunctionId) -> Visibility;
 
-    #[salsa::invoke(visibility::const_visibility_query)]
-    fn const_visibility(&self, def: ConstId) -> Visibility;
-
     #[salsa::transparent]
     fn crate_limits(&self, crate_id: CrateId) -> CrateLimits;
 }
diff --git a/crates/hir_def/src/visibility.rs b/crates/hir_def/src/visibility.rs
index 6e22a877a9..f76034a3e2 100644
--- a/crates/hir_def/src/visibility.rs
+++ b/crates/hir_def/src/visibility.rs
@@ -11,7 +11,7 @@ use crate::{
     nameres::DefMap,
     path::{ModPath, PathKind},
     resolver::HasResolver,
-    ConstId, FunctionId, HasModule, LocalFieldId, ModuleId, VariantId,
+    FunctionId, HasModule, LocalFieldId, ModuleId, VariantId,
 };
 
 /// Visibility of an item, not yet resolved.
@@ -234,9 +234,3 @@ pub(crate) fn function_visibility_query(db: &dyn DefDatabase, def: FunctionId) -
     let resolver = def.resolver(db);
     db.function_data(def).visibility.resolve(db, &resolver)
 }
-
-/// Resolve visibility of a const.
-pub(crate) fn const_visibility_query(db: &dyn DefDatabase, def: ConstId) -> Visibility {
-    let resolver = def.resolver(db);
-    db.const_data(def).visibility.resolve(db, &resolver)
-}
diff --git a/crates/hir_ty/src/builder.rs b/crates/hir_ty/src/builder.rs
index 5c77c0cdf8..c507c42f5b 100644
--- a/crates/hir_ty/src/builder.rs
+++ b/crates/hir_ty/src/builder.rs
@@ -15,10 +15,10 @@ use hir_def::{
 use smallvec::SmallVec;
 
 use crate::{
-    consteval::unknown_const_as_generic, db::HirDatabase, infer::unify::InferenceTable, primitive,
-    to_assoc_type_id, to_chalk_trait_id, utils::generics, Binders, CallableSig, ConstData,
-    ConstValue, GenericArg, GenericArgData, Interner, ProjectionTy, Substitution, TraitRef, Ty,
-    TyDefId, TyExt, TyKind, ValueTyDefId,
+    consteval::unknown_const_as_generic, db::HirDatabase, primitive, to_assoc_type_id,
+    to_chalk_trait_id, utils::generics, Binders, CallableSig, ConstData, ConstValue, GenericArg,
+    GenericArgData, Interner, ProjectionTy, Substitution, TraitRef, Ty, TyDefId, TyExt, TyKind,
+    ValueTyDefId,
 };
 
 #[derive(Debug, Clone, PartialEq, Eq)]
@@ -111,15 +111,6 @@ impl<D> TyBuilder<D> {
         this
     }
 
-    pub(crate) fn fill_with_inference_vars(self, table: &mut InferenceTable) -> Self {
-        self.fill(|x| match x {
-            ParamKind::Type => GenericArgData::Ty(table.new_type_var()).intern(Interner),
-            ParamKind::Const(ty) => {
-                GenericArgData::Const(table.new_const_var(ty.clone())).intern(Interner)
-            }
-        })
-    }
-
     pub fn fill(mut self, filler: impl FnMut(&ParamKind) -> GenericArg) -> Self {
         self.vec.extend(self.param_kinds.iter().skip(self.vec.len()).map(filler));
         assert_eq!(self.remaining(), 0);
diff --git a/crates/hir_ty/src/infer.rs b/crates/hir_ty/src/infer.rs
index 442774d0be..4ee5391057 100644
--- a/crates/hir_ty/src/infer.rs
+++ b/crates/hir_ty/src/infer.rs
@@ -35,9 +35,10 @@ use rustc_hash::FxHashMap;
 use stdx::impl_from;
 
 use crate::{
-    db::HirDatabase, fold_tys_and_consts, infer::coerce::CoerceMany, lower::ImplTraitLoweringMode,
-    to_assoc_type_id, AliasEq, AliasTy, Const, DomainGoal, GenericArg, Goal, InEnvironment,
-    Interner, ProjectionTy, Substitution, TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind,
+    builder::ParamKind, db::HirDatabase, fold_tys_and_consts, infer::coerce::CoerceMany,
+    lower::ImplTraitLoweringMode, to_assoc_type_id, AliasEq, AliasTy, Const, DomainGoal,
+    GenericArg, GenericArgData, Goal, InEnvironment, Interner, ProjectionTy, Substitution,
+    TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind,
 };
 
 // This lint has a false positive here. See the link below for details.
@@ -45,6 +46,7 @@ use crate::{
 // https://github.com/rust-lang/rust/issues/57411
 #[allow(unreachable_pub)]
 pub use unify::could_unify;
+pub(crate) use unify::unify;
 
 pub(crate) mod unify;
 mod path;
@@ -655,7 +657,15 @@ impl<'a> InferenceContext<'a> {
             }
             TypeNs::TypeAliasId(it) => {
                 let ty = TyBuilder::def_ty(self.db, it.into())
-                    .fill_with_inference_vars(&mut self.table)
+                    .fill(|x| match x {
+                        ParamKind::Type => {
+                            GenericArgData::Ty(self.table.new_type_var()).intern(Interner)
+                        }
+                        ParamKind::Const(ty) => {
+                            GenericArgData::Const(self.table.new_const_var(ty.clone()))
+                                .intern(Interner)
+                        }
+                    })
                     .build();
                 self.resolve_variant_on_alias(ty, unresolved, path)
             }
diff --git a/crates/hir_ty/src/infer/path.rs b/crates/hir_ty/src/infer/path.rs
index 53fe2131a3..1d23654576 100644
--- a/crates/hir_ty/src/infer/path.rs
+++ b/crates/hir_ty/src/infer/path.rs
@@ -104,7 +104,9 @@ impl<'a> InferenceContext<'a> {
                     ParamKind::Type => {
                         GenericArgData::Ty(TyKind::Error.intern(Interner)).intern(Interner)
                     }
-                    ParamKind::Const(ty) => consteval::unknown_const_as_generic(ty.clone()),
+                    ParamKind::Const(_) => {
+                        GenericArgData::Const(consteval::usize_const(None)).intern(Interner)
+                    }
                 })
             })
             .build();
@@ -247,7 +249,15 @@ impl<'a> InferenceContext<'a> {
                 let substs = match container {
                     ItemContainerId::ImplId(impl_id) => {
                         let impl_substs = TyBuilder::subst_for_def(self.db, impl_id)
-                            .fill_with_inference_vars(&mut self.table)
+                            .fill(|x| match x {
+                                ParamKind::Type => {
+                                    GenericArgData::Ty(self.table.new_type_var()).intern(Interner)
+                                }
+                                ParamKind::Const(ty) => {
+                                    GenericArgData::Const(self.table.new_const_var(ty.clone()))
+                                        .intern(Interner)
+                                }
+                            })
                             .build();
                         let impl_self_ty =
                             self.db.impl_self_ty(impl_id).substitute(Interner, &impl_substs);
@@ -258,7 +268,15 @@ impl<'a> InferenceContext<'a> {
                         // we're picking this method
                         let trait_ref = TyBuilder::trait_ref(self.db, trait_)
                             .push(ty.clone())
-                            .fill_with_inference_vars(&mut self.table)
+                            .fill(|x| match x {
+                                ParamKind::Type => {
+                                    GenericArgData::Ty(self.table.new_type_var()).intern(Interner)
+                                }
+                                ParamKind::Const(ty) => {
+                                    GenericArgData::Const(self.table.new_const_var(ty.clone()))
+                                        .intern(Interner)
+                                }
+                            })
                             .build();
                         self.push_obligation(trait_ref.clone().cast(Interner));
                         Some(trait_ref.substitution)
diff --git a/crates/hir_ty/src/infer/unify.rs b/crates/hir_ty/src/infer/unify.rs
index ef0675d59f..deb536e511 100644
--- a/crates/hir_ty/src/infer/unify.rs
+++ b/crates/hir_ty/src/infer/unify.rs
@@ -146,7 +146,7 @@ pub(crate) struct InferenceTable<'a> {
 
 pub(crate) struct InferenceTableSnapshot {
     var_table_snapshot: chalk_solve::infer::InferenceSnapshot<Interner>,
-    pending_obligations: Vec<Canonicalized<InEnvironment<Goal>>>,
+    // FIXME: snapshot pending_obligations?
     type_variable_table_snapshot: Vec<TypeVariableData>,
 }
 
@@ -365,25 +365,12 @@ impl<'a> InferenceTable<'a> {
     pub(crate) fn snapshot(&mut self) -> InferenceTableSnapshot {
         let var_table_snapshot = self.var_unification_table.snapshot();
         let type_variable_table_snapshot = self.type_variable_table.clone();
-        let pending_obligations = self.pending_obligations.clone();
-        InferenceTableSnapshot {
-            var_table_snapshot,
-            pending_obligations,
-            type_variable_table_snapshot,
-        }
+        InferenceTableSnapshot { var_table_snapshot, type_variable_table_snapshot }
     }
 
     pub(crate) fn rollback_to(&mut self, snapshot: InferenceTableSnapshot) {
         self.var_unification_table.rollback_to(snapshot.var_table_snapshot);
         self.type_variable_table = snapshot.type_variable_table_snapshot;
-        self.pending_obligations = snapshot.pending_obligations;
-    }
-
-    pub(crate) fn run_in_snapshot<T>(&mut self, f: impl FnOnce(&mut InferenceTable) -> T) -> T {
-        let snapshot = self.snapshot();
-        let result = f(self);
-        self.rollback_to(snapshot);
-        result
     }
 
     /// Checks an obligation without registering it. Useful mostly to check
diff --git a/crates/hir_ty/src/lower.rs b/crates/hir_ty/src/lower.rs
index 41bb94c5d5..9e69785ac2 100644
--- a/crates/hir_ty/src/lower.rs
+++ b/crates/hir_ty/src/lower.rs
@@ -9,8 +9,6 @@ use std::cell::{Cell, RefCell};
 use std::{iter, sync::Arc};
 
 use base_db::CrateId;
-use chalk_ir::fold::Fold;
-use chalk_ir::interner::HasInterner;
 use chalk_ir::{cast::Cast, fold::Shift, Mutability, Safety};
 use hir_def::generics::TypeOrConstParamData;
 use hir_def::intern::Interned;
@@ -38,6 +36,7 @@ use stdx::{impl_from, never};
 use syntax::{ast, SmolStr};
 
 use crate::consteval::{path_to_const, unknown_const_as_generic, unknown_const_usize, usize_const};
+use crate::method_resolution::fallback_bound_vars;
 use crate::utils::Generics;
 use crate::{all_super_traits, make_binders, Const, GenericArgData, ParamKind};
 use crate::{
@@ -713,7 +712,7 @@ impl<'a> TyLoweringContext<'a> {
                 .filter(|arg| !matches!(arg, GenericArg::Lifetime(_)))
                 .skip(skip)
                 .take(expected_num)
-                .zip(def_generics.iter_id().skip(parent_params + skip))
+                .zip(def_generics.iter_id().skip(skip))
             {
                 if let Some(x) = generic_arg_to_chalk(
                     self.db,
@@ -1702,28 +1701,3 @@ pub(crate) fn const_or_path_to_chalk(
         }
     }
 }
-
-/// This replaces any 'free' Bound vars in `s` (i.e. those with indices past
-/// num_vars_to_keep) by `TyKind::Unknown`.
-fn fallback_bound_vars<T: Fold<Interner> + HasInterner<Interner = Interner>>(
-    s: T,
-    num_vars_to_keep: usize,
-) -> T::Result {
-    crate::fold_free_vars(
-        s,
-        |bound, binders| {
-            if bound.index >= num_vars_to_keep && bound.debruijn == DebruijnIndex::INNERMOST {
-                TyKind::Error.intern(Interner)
-            } else {
-                bound.shifted_in_from(binders).to_ty(Interner)
-            }
-        },
-        |ty, bound, binders| {
-            if bound.index >= num_vars_to_keep && bound.debruijn == DebruijnIndex::INNERMOST {
-                consteval::unknown_const(ty.clone())
-            } else {
-                bound.shifted_in_from(binders).to_const(Interner, ty)
-            }
-        },
-    )
-}
diff --git a/crates/hir_ty/src/method_resolution.rs b/crates/hir_ty/src/method_resolution.rs
index 1c939f3d8a..06c834fbc8 100644
--- a/crates/hir_ty/src/method_resolution.rs
+++ b/crates/hir_ty/src/method_resolution.rs
@@ -6,7 +6,7 @@ use std::{iter, ops::ControlFlow, sync::Arc};
 
 use arrayvec::ArrayVec;
 use base_db::{CrateId, Edition};
-use chalk_ir::{cast::Cast, Mutability, UniverseIndex};
+use chalk_ir::{cast::Cast, fold::Fold, interner::HasInterner, Mutability, UniverseIndex};
 use hir_def::{
     item_scope::ItemScope, lang_item::LangItemTarget, nameres::DefMap, AssocItemId, BlockId,
     ConstId, FunctionId, GenericDefId, HasModule, ImplId, ItemContainerId, Lookup, ModuleDefId,
@@ -18,14 +18,16 @@ use stdx::never;
 
 use crate::{
     autoderef::{self, AutoderefKind},
+    consteval::{self, ConstExt},
     db::HirDatabase,
     from_foreign_def_id,
     infer::{unify::InferenceTable, Adjust, Adjustment, AutoBorrow, OverloadedDeref, PointerCast},
     primitive::{self, FloatTy, IntTy, UintTy},
     static_lifetime,
     utils::all_super_traits,
-    AdtId, Canonical, CanonicalVarKinds, DebruijnIndex, ForeignDefId, InEnvironment, Interner,
-    Scalar, TraitEnvironment, TraitRefExt, Ty, TyBuilder, TyExt, TyKind,
+    AdtId, Canonical, CanonicalVarKinds, DebruijnIndex, ForeignDefId, GenericArgData,
+    InEnvironment, Interner, Scalar, Substitution, TraitEnvironment, TraitRefExt, Ty, TyBuilder,
+    TyExt, TyKind,
 };
 
 /// This is used as a key for indexing impls.
@@ -641,10 +643,11 @@ pub fn iterate_method_candidates_dyn(
             let mut table = InferenceTable::new(db, env.clone());
             let ty = table.instantiate_canonical(ty.clone());
             let (deref_chain, adj) = autoderef_method_receiver(&mut table, ty);
+            let deref_chains = stdx::slice_tails(&deref_chain);
 
-            let result = deref_chain.into_iter().zip(adj).try_for_each(|(receiver_ty, adj)| {
+            let result = deref_chains.zip(adj).try_for_each(|(deref_chain, adj)| {
                 iterate_method_candidates_with_autoref(
-                    &receiver_ty,
+                    deref_chain,
                     adj,
                     db,
                     env.clone(),
@@ -672,7 +675,7 @@ pub fn iterate_method_candidates_dyn(
 }
 
 fn iterate_method_candidates_with_autoref(
-    receiver_ty: &Canonical<Ty>,
+    deref_chain: &[Canonical<Ty>],
     first_adjustment: ReceiverAdjustments,
     db: &dyn HirDatabase,
     env: Arc<TraitEnvironment>,
@@ -681,9 +684,17 @@ fn iterate_method_candidates_with_autoref(
     name: Option<&Name>,
     mut callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId) -> ControlFlow<()>,
 ) -> ControlFlow<()> {
+    let (receiver_ty, rest) = match deref_chain.split_first() {
+        Some((rec, rest)) => (rec, rest),
+        None => {
+            never!("received empty deref-chain");
+            return ControlFlow::Break(());
+        }
+    };
     iterate_method_candidates_by_receiver(
         receiver_ty,
         first_adjustment.clone(),
+        rest,
         db,
         env.clone(),
         traits_in_scope,
@@ -701,6 +712,7 @@ fn iterate_method_candidates_with_autoref(
     iterate_method_candidates_by_receiver(
         &refed,
         first_adjustment.with_autoref(Mutability::Not),
+        deref_chain,
         db,
         env.clone(),
         traits_in_scope,
@@ -718,6 +730,7 @@ fn iterate_method_candidates_with_autoref(
     iterate_method_candidates_by_receiver(
         &ref_muted,
         first_adjustment.with_autoref(Mutability::Mut),
+        deref_chain,
         db,
         env,
         traits_in_scope,
@@ -730,6 +743,7 @@ fn iterate_method_candidates_with_autoref(
 fn iterate_method_candidates_by_receiver(
     receiver_ty: &Canonical<Ty>,
     receiver_adjustments: ReceiverAdjustments,
+    rest_of_deref_chain: &[Canonical<Ty>],
     db: &dyn HirDatabase,
     env: Arc<TraitEnvironment>,
     traits_in_scope: &FxHashSet<TraitId>,
@@ -737,35 +751,30 @@ fn iterate_method_candidates_by_receiver(
     name: Option<&Name>,
     mut callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId) -> ControlFlow<()>,
 ) -> ControlFlow<()> {
-    let mut table = InferenceTable::new(db, env);
-    let receiver_ty = table.instantiate_canonical(receiver_ty.clone());
-    let snapshot = table.snapshot();
     // We're looking for methods with *receiver* type receiver_ty. These could
     // be found in any of the derefs of receiver_ty, so we have to go through
     // that.
-    let mut autoderef = autoderef::Autoderef::new(&mut table, receiver_ty.clone());
-    while let Some((self_ty, _)) = autoderef.next() {
+    for self_ty in iter::once(receiver_ty).chain(rest_of_deref_chain) {
         iterate_inherent_methods(
-            &self_ty,
-            &mut autoderef.table,
+            self_ty,
+            db,
+            env.clone(),
             name,
-            Some(&receiver_ty),
+            Some(receiver_ty),
             Some(receiver_adjustments.clone()),
             visible_from_module,
             &mut callback,
         )?
     }
 
-    table.rollback_to(snapshot);
-
-    let mut autoderef = autoderef::Autoderef::new(&mut table, receiver_ty.clone());
-    while let Some((self_ty, _)) = autoderef.next() {
+    for self_ty in iter::once(receiver_ty).chain(rest_of_deref_chain) {
         iterate_trait_method_candidates(
-            &self_ty,
-            &mut autoderef.table,
+            self_ty,
+            db,
+            env.clone(),
             traits_in_scope,
             name,
-            Some(&receiver_ty),
+            Some(receiver_ty),
             Some(receiver_adjustments.clone()),
             &mut callback,
         )?
@@ -783,55 +792,43 @@ fn iterate_method_candidates_for_self_ty(
     name: Option<&Name>,
     mut callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId) -> ControlFlow<()>,
 ) -> ControlFlow<()> {
-    let mut table = InferenceTable::new(db, env);
-    let self_ty = table.instantiate_canonical(self_ty.clone());
     iterate_inherent_methods(
-        &self_ty,
-        &mut table,
+        self_ty,
+        db,
+        env.clone(),
         name,
         None,
         None,
         visible_from_module,
         &mut callback,
     )?;
-    iterate_trait_method_candidates(
-        &self_ty,
-        &mut table,
-        traits_in_scope,
-        name,
-        None,
-        None,
-        callback,
-    )
+    iterate_trait_method_candidates(self_ty, db, env, traits_in_scope, name, None, None, callback)
 }
 
 fn iterate_trait_method_candidates(
-    self_ty: &Ty,
-    table: &mut InferenceTable,
+    self_ty: &Canonical<Ty>,
+    db: &dyn HirDatabase,
+    env: Arc<TraitEnvironment>,
     traits_in_scope: &FxHashSet<TraitId>,
     name: Option<&Name>,
-    receiver_ty: Option<&Ty>,
+    receiver_ty: Option<&Canonical<Ty>>,
     receiver_adjustments: Option<ReceiverAdjustments>,
     callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId) -> ControlFlow<()>,
 ) -> ControlFlow<()> {
-    let db = table.db;
-    let env = table.trait_env.clone();
-    let self_is_array = matches!(self_ty.kind(Interner), chalk_ir::TyKind::Array(..));
+    let self_is_array = matches!(self_ty.value.kind(Interner), chalk_ir::TyKind::Array(..));
     // if ty is `dyn Trait`, the trait doesn't need to be in scope
     let inherent_trait =
-        self_ty.dyn_trait().into_iter().flat_map(|t| all_super_traits(db.upcast(), t));
-    let env_traits = matches!(self_ty.kind(Interner), TyKind::Placeholder(_))
+        self_ty.value.dyn_trait().into_iter().flat_map(|t| all_super_traits(db.upcast(), t));
+    let env_traits = matches!(self_ty.value.kind(Interner), TyKind::Placeholder(_))
         // if we have `T: Trait` in the param env, the trait doesn't need to be in scope
         .then(|| {
-            env.traits_in_scope_from_clauses(self_ty.clone())
+            env.traits_in_scope_from_clauses(self_ty.value.clone())
                 .flat_map(|t| all_super_traits(db.upcast(), t))
         })
         .into_iter()
         .flatten();
     let traits = inherent_trait.chain(env_traits).chain(traits_in_scope.iter().copied());
 
-    let canonical_self_ty = table.canonicalize(self_ty.clone()).value;
-
     'traits: for t in traits {
         let data = db.trait_data(t);
 
@@ -855,11 +852,11 @@ fn iterate_trait_method_candidates(
         for &(_, item) in data.items.iter() {
             // Don't pass a `visible_from_module` down to `is_valid_candidate`,
             // since only inherent methods should be included into visibility checking.
-            if !is_valid_candidate(table, name, receiver_ty, item, self_ty, None) {
+            if !is_valid_candidate(db, env.clone(), name, receiver_ty, item, self_ty, None) {
                 continue;
             }
             if !known_implemented {
-                let goal = generic_implements_goal(db, env.clone(), t, &canonical_self_ty);
+                let goal = generic_implements_goal(db, env.clone(), t, self_ty);
                 if db.trait_solve(env.krate, goal.cast(Interner)).is_none() {
                     continue 'traits;
                 }
@@ -871,18 +868,40 @@ fn iterate_trait_method_candidates(
     ControlFlow::Continue(())
 }
 
-fn iterate_inherent_methods(
+fn filter_inherent_impls_for_self_ty<'i>(
+    impls: &'i InherentImpls,
     self_ty: &Ty,
-    table: &mut InferenceTable,
+) -> impl Iterator<Item = &'i ImplId> {
+    // inherent methods on arrays are fingerprinted as [T; {unknown}], so we must also consider them when
+    // resolving a method call on an array with a known len
+    let array_impls = {
+        match self_ty.kind(Interner) {
+            TyKind::Array(parameters, array_len) if !array_len.is_unknown() => {
+                let unknown_array_len_ty =
+                    TyKind::Array(parameters.clone(), consteval::usize_const(None));
+
+                Some(impls.for_self_ty(&unknown_array_len_ty.intern(Interner)))
+            }
+            _ => None,
+        }
+    }
+    .into_iter()
+    .flatten();
+
+    impls.for_self_ty(self_ty).iter().chain(array_impls)
+}
+
+fn iterate_inherent_methods(
+    self_ty: &Canonical<Ty>,
+    db: &dyn HirDatabase,
+    env: Arc<TraitEnvironment>,
     name: Option<&Name>,
-    receiver_ty: Option<&Ty>,
+    receiver_ty: Option<&Canonical<Ty>>,
     receiver_adjustments: Option<ReceiverAdjustments>,
     visible_from_module: VisibleFromModule,
     callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId) -> ControlFlow<()>,
 ) -> ControlFlow<()> {
-    let db = table.db;
-    let env = table.trait_env.clone();
-    let def_crates = match def_crates(db, self_ty, env.krate) {
+    let def_crates = match def_crates(db, &self_ty.value, env.krate) {
         Some(k) => k,
         None => return ControlFlow::Continue(()),
     };
@@ -898,7 +917,8 @@ fn iterate_inherent_methods(
             impls_for_self_ty(
                 &impls,
                 self_ty,
-                table,
+                db,
+                env.clone(),
                 name,
                 receiver_ty,
                 receiver_adjustments.clone(),
@@ -913,7 +933,8 @@ fn iterate_inherent_methods(
         impls_for_self_ty(
             &impls,
             self_ty,
-            table,
+            db,
+            env.clone(),
             name,
             receiver_ty,
             receiver_adjustments.clone(),
@@ -925,20 +946,37 @@ fn iterate_inherent_methods(
 
     fn impls_for_self_ty(
         impls: &InherentImpls,
-        self_ty: &Ty,
-        table: &mut InferenceTable,
+        self_ty: &Canonical<Ty>,
+        db: &dyn HirDatabase,
+        env: Arc<TraitEnvironment>,
         name: Option<&Name>,
-        receiver_ty: Option<&Ty>,
+        receiver_ty: Option<&Canonical<Ty>>,
         receiver_adjustments: Option<ReceiverAdjustments>,
         visible_from_module: Option<ModuleId>,
         callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId) -> ControlFlow<()>,
     ) -> ControlFlow<()> {
-        let db = table.db;
-        let impls_for_self_ty = impls.for_self_ty(self_ty);
+        let impls_for_self_ty = filter_inherent_impls_for_self_ty(impls, &self_ty.value);
         for &impl_def in impls_for_self_ty {
             for &item in &db.impl_data(impl_def).items {
-                if !is_valid_candidate(table, name, receiver_ty, item, self_ty, visible_from_module)
+                if !is_valid_candidate(
+                    db,
+                    env.clone(),
+                    name,
+                    receiver_ty,
+                    item,
+                    self_ty,
+                    visible_from_module,
+                ) {
+                    continue;
+                }
+                // we have to check whether the self type unifies with the type
+                // that the impl is for. If we have a receiver type, this
+                // already happens in `is_valid_candidate` above; if not, we
+                // check it here
+                if receiver_ty.is_none()
+                    && inherent_impl_substs(db, env.clone(), impl_def, self_ty).is_none()
                 {
+                    cov_mark::hit!(impl_self_type_match_without_receiver);
                     continue;
                 }
                 callback(receiver_adjustments.clone().unwrap_or_default(), item)?;
@@ -967,15 +1005,37 @@ pub fn resolve_indexing_op(
     None
 }
 
+fn is_transformed_receiver_ty_equal(transformed_receiver_ty: &Ty, receiver_ty: &Ty) -> bool {
+    if transformed_receiver_ty == receiver_ty {
+        return true;
+    }
+
+    // a transformed receiver may be considered equal (and a valid method call candidate) if it is an array
+    // with an unknown (i.e. generic) length, and the receiver is an array with the same item type but a known len,
+    // this allows inherent methods on arrays to be considered valid resolution candidates
+    match (transformed_receiver_ty.kind(Interner), receiver_ty.kind(Interner)) {
+        (
+            TyKind::Array(transformed_array_ty, transformed_array_len),
+            TyKind::Array(receiver_array_ty, receiver_array_len),
+        ) if transformed_array_ty == receiver_array_ty
+            && transformed_array_len.is_unknown()
+            && !receiver_array_len.is_unknown() =>
+        {
+            true
+        }
+        _ => false,
+    }
+}
+
 fn is_valid_candidate(
-    table: &mut InferenceTable,
+    db: &dyn HirDatabase,
+    env: Arc<TraitEnvironment>,
     name: Option<&Name>,
-    receiver_ty: Option<&Ty>,
+    receiver_ty: Option<&Canonical<Ty>>,
     item: AssocItemId,
-    self_ty: &Ty,
+    self_ty: &Canonical<Ty>,
     visible_from_module: Option<ModuleId>,
 ) -> bool {
-    let db = table.db;
     match item {
         AssocItemId::FunctionId(m) => {
             let data = db.function_data(m);
@@ -984,82 +1044,123 @@ fn is_valid_candidate(
                     return false;
                 }
             }
-            table.run_in_snapshot(|table| {
-                let subst = TyBuilder::subst_for_def(db, m).fill_with_inference_vars(table).build();
-                let expected_self_ty = match m.lookup(db.upcast()).container {
-                    ItemContainerId::TraitId(_) => {
-                        subst.at(Interner, 0).assert_ty_ref(Interner).clone()
-                    }
-                    ItemContainerId::ImplId(impl_id) => {
-                        subst.apply(db.impl_self_ty(impl_id).skip_binders().clone(), Interner)
-                    }
-                    // We should only get called for associated items (impl/trait)
-                    ItemContainerId::ModuleId(_) | ItemContainerId::ExternBlockId(_) => {
-                        unreachable!()
-                    }
-                };
-                if !table.unify(&expected_self_ty, &self_ty) {
+            if let Some(receiver_ty) = receiver_ty {
+                if !data.has_self_param() {
                     return false;
                 }
-                if let Some(receiver_ty) = receiver_ty {
-                    if !data.has_self_param() {
-                        return false;
-                    }
-
-                    let sig = db.callable_item_signature(m.into());
-                    let expected_receiver =
-                        sig.map(|s| s.params()[0].clone()).substitute(Interner, &subst);
-                    let receiver_matches = table.unify(&receiver_ty, &expected_receiver);
-
-                    if !receiver_matches {
-                        return false;
-                    }
-                }
-                if let Some(from_module) = visible_from_module {
-                    if !db.function_visibility(m).is_visible_from(db.upcast(), from_module) {
-                        cov_mark::hit!(autoderef_candidate_not_visible);
-                        return false;
-                    }
-                }
+                let transformed_receiver_ty = match transform_receiver_ty(db, env, m, self_ty) {
+                    Some(ty) => ty,
+                    None => return false,
+                };
 
-                true
-            })
-        }
-        AssocItemId::ConstId(c) => {
-            let data = db.const_data(c);
-            if receiver_ty.is_some() {
-                return false;
-            }
-            if let Some(name) = name {
-                if data.name.as_ref() != Some(name) {
+                if !is_transformed_receiver_ty_equal(&transformed_receiver_ty, &receiver_ty.value) {
                     return false;
                 }
             }
             if let Some(from_module) = visible_from_module {
-                if !db.const_visibility(c).is_visible_from(db.upcast(), from_module) {
-                    cov_mark::hit!(const_candidate_not_visible);
-                    return false;
-                }
-            }
-            if let ItemContainerId::ImplId(impl_id) = c.lookup(db.upcast()).container {
-                let self_ty_matches = table.run_in_snapshot(|table| {
-                    let subst =
-                        TyBuilder::subst_for_def(db, c).fill_with_inference_vars(table).build();
-                    let expected_self_ty =
-                        subst.apply(db.impl_self_ty(impl_id).skip_binders().clone(), Interner);
-                    table.unify(&expected_self_ty, &self_ty)
-                });
-                if !self_ty_matches {
-                    cov_mark::hit!(const_candidate_self_type_mismatch);
+                if !db.function_visibility(m).is_visible_from(db.upcast(), from_module) {
+                    cov_mark::hit!(autoderef_candidate_not_visible);
                     return false;
                 }
             }
+
             true
         }
+        AssocItemId::ConstId(c) => {
+            let data = db.const_data(c);
+            name.map_or(true, |name| data.name.as_ref() == Some(name)) && receiver_ty.is_none()
+        }
         _ => false,
     }
 }
 
+pub(crate) fn inherent_impl_substs(
+    db: &dyn HirDatabase,
+    env: Arc<TraitEnvironment>,
+    impl_id: ImplId,
+    self_ty: &Canonical<Ty>,
+) -> Option<Substitution> {
+    // we create a var for each type parameter of the impl; we need to keep in
+    // mind here that `self_ty` might have vars of its own
+    let self_ty_vars = self_ty.binders.len(Interner);
+    let vars = TyBuilder::subst_for_def(db, impl_id)
+        .fill_with_bound_vars(DebruijnIndex::INNERMOST, self_ty_vars)
+        .build();
+    let self_ty_with_vars = db.impl_self_ty(impl_id).substitute(Interner, &vars);
+    let mut kinds = self_ty.binders.interned().to_vec();
+    kinds.extend(vars.iter(Interner).map(|x| {
+        let kind = match x.data(Interner) {
+            GenericArgData::Ty(_) => chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General),
+            GenericArgData::Const(c) => chalk_ir::VariableKind::Const(c.data(Interner).ty.clone()),
+            GenericArgData::Lifetime(_) => chalk_ir::VariableKind::Lifetime,
+        };
+        chalk_ir::WithKind::new(kind, UniverseIndex::ROOT)
+    }));
+    let tys = Canonical {
+        binders: CanonicalVarKinds::from_iter(Interner, kinds),
+        value: (self_ty_with_vars, self_ty.value.clone()),
+    };
+    let substs = super::infer::unify(db, env, &tys)?;
+    // We only want the substs for the vars we added, not the ones from self_ty.
+    // Also, if any of the vars we added are still in there, we replace them by
+    // Unknown. I think this can only really happen if self_ty contained
+    // Unknown, and in that case we want the result to contain Unknown in those
+    // places again.
+    let suffix =
+        Substitution::from_iter(Interner, substs.iter(Interner).skip(self_ty_vars).cloned());
+    Some(fallback_bound_vars(suffix, self_ty_vars))
+}
+
+/// This replaces any 'free' Bound vars in `s` (i.e. those with indices past
+/// num_vars_to_keep) by `TyKind::Unknown`.
+pub(crate) fn fallback_bound_vars<T: Fold<Interner> + HasInterner<Interner = Interner>>(
+    s: T,
+    num_vars_to_keep: usize,
+) -> T::Result {
+    crate::fold_free_vars(
+        s,
+        |bound, binders| {
+            if bound.index >= num_vars_to_keep && bound.debruijn == DebruijnIndex::INNERMOST {
+                TyKind::Error.intern(Interner)
+            } else {
+                bound.shifted_in_from(binders).to_ty(Interner)
+            }
+        },
+        |ty, bound, binders| {
+            if bound.index >= num_vars_to_keep && bound.debruijn == DebruijnIndex::INNERMOST {
+                consteval::usize_const(None)
+            } else {
+                bound.shifted_in_from(binders).to_const(Interner, ty)
+            }
+        },
+    )
+}
+
+fn transform_receiver_ty(
+    db: &dyn HirDatabase,
+    env: Arc<TraitEnvironment>,
+    function_id: FunctionId,
+    self_ty: &Canonical<Ty>,
+) -> Option<Ty> {
+    let substs = match function_id.lookup(db.upcast()).container {
+        ItemContainerId::TraitId(_) => TyBuilder::subst_for_def(db, function_id)
+            .push(self_ty.value.clone())
+            .fill_with_unknown()
+            .build(),
+        ItemContainerId::ImplId(impl_id) => {
+            let impl_substs = inherent_impl_substs(db, env, impl_id, self_ty)?;
+            TyBuilder::subst_for_def(db, function_id)
+                .use_parent_substs(&impl_substs)
+                .fill_with_unknown()
+                .build()
+        }
+        // No receiver
+        ItemContainerId::ModuleId(_) | ItemContainerId::ExternBlockId(_) => unreachable!(),
+    };
+    let sig = db.callable_item_signature(function_id.into());
+    Some(sig.map(|s| s.params()[0].clone()).substitute(Interner, &substs))
+}
+
 pub fn implements_trait(
     ty: &Canonical<Ty>,
     db: &dyn HirDatabase,
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs
index d04bd87b7b..13e7a0ac88 100644
--- a/crates/ide/src/inlay_hints.rs
+++ b/crates/ide/src/inlay_hints.rs
@@ -1,10 +1,13 @@
 use either::Either;
 use hir::{known, Callable, HasVisibility, HirDisplay, Semantics, TypeInfo};
-use ide_db::{base_db::FileRange, famous_defs::FamousDefs, RootDatabase};
+use ide_db::{
+    base_db::FileRange, famous_defs::FamousDefs, syntax_helpers::node_ext::walk_ty, RootDatabase,
+};
 use itertools::Itertools;
+use rustc_hash::FxHashMap;
 use stdx::to_lower_snake_case;
 use syntax::{
-    ast::{self, AstNode, HasArgList, HasName, UnaryOp},
+    ast::{self, AstNode, HasArgList, HasGenericParams, HasName, UnaryOp},
     match_ast, Direction, NodeOrToken, SmolStr, SyntaxKind, SyntaxNode, TextRange, T,
 };
 
@@ -17,16 +20,27 @@ pub struct InlayHintsConfig {
     pub parameter_hints: bool,
     pub chaining_hints: bool,
     pub closure_return_type_hints: bool,
+    pub lifetime_elision_hints: LifetimeElisionHints,
+    pub param_names_for_lifetime_elision_hints: bool,
     pub hide_named_constructor_hints: bool,
     pub max_length: Option<usize>,
 }
 
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub enum LifetimeElisionHints {
+    Always,
+    SkipTrivial,
+    Never,
+}
+
 #[derive(Clone, Debug, PartialEq, Eq)]
 pub enum InlayKind {
     TypeHint,
     ParameterHint,
     ClosureReturnTypeHint,
     ChainingHint,
+    GenericParamListHint,
+    LifetimeHint,
 }
 
 #[derive(Debug)]
@@ -41,12 +55,17 @@ pub struct InlayHint {
 // rust-analyzer shows additional information inline with the source code.
 // Editors usually render this using read-only virtual text snippets interspersed with code.
 //
-// rust-analyzer shows hints for
+// rust-analyzer by default shows hints for
 //
 // * types of local variables
 // * names of function arguments
 // * types of chained expressions
 //
+// Optionally, one can enable additional hints for
+//
+// * return types of closure expressions with blocks
+// * elided lifetimes
+//
 // **Note:** VS Code does not have native support for inlay hints https://github.com/microsoft/vscode/issues/16221[yet] and the hints are implemented using decorations.
 // This approach has limitations, the caret movement and bracket highlighting near the edges of the hint may be weird:
 // https://github.com/rust-analyzer/rust-analyzer/issues/1623[1], https://github.com/rust-analyzer/rust-analyzer/issues/3453[2].
@@ -108,9 +127,210 @@ fn hints(
             }
             _ => (),
         }
-    } else if let Some(it) = ast::IdentPat::cast(node) {
+    } else if let Some(it) = ast::IdentPat::cast(node.clone()) {
         bind_pat_hints(hints, sema, config, &it);
+    } else if let Some(it) = ast::Fn::cast(node) {
+        lifetime_hints(hints, config, it);
+    }
+}
+
+fn lifetime_hints(
+    acc: &mut Vec<InlayHint>,
+    config: &InlayHintsConfig,
+    func: ast::Fn,
+) -> Option<()> {
+    if config.lifetime_elision_hints == LifetimeElisionHints::Never {
+        return None;
+    }
+    let param_list = func.param_list()?;
+    let generic_param_list = func.generic_param_list();
+    let ret_type = func.ret_type();
+    let self_param = param_list.self_param().filter(|it| it.amp_token().is_some());
+
+    let mut used_names: FxHashMap<SmolStr, usize> = generic_param_list
+        .iter()
+        .filter(|_| config.param_names_for_lifetime_elision_hints)
+        .flat_map(|gpl| gpl.lifetime_params())
+        .filter_map(|param| param.lifetime())
+        .filter_map(|lt| Some((SmolStr::from(lt.text().as_str().get(1..)?), 0)))
+        .collect();
+
+    let mut allocated_lifetimes = vec![];
+    let mut gen_idx_name = {
+        let mut gen = (0u8..).map(|idx| match idx {
+            idx if idx < 10 => SmolStr::from_iter(['\'', (idx + 48) as char]),
+            idx => format!("'{idx}").into(),
+        });
+        move || gen.next().unwrap_or_default()
+    };
+
+    let mut potential_lt_refs: Vec<_> = vec![];
+    param_list
+        .params()
+        .filter_map(|it| {
+            Some((
+                config.param_names_for_lifetime_elision_hints.then(|| it.pat()).flatten(),
+                it.ty()?,
+            ))
+        })
+        .for_each(|(pat, ty)| {
+            // FIXME: check path types
+            walk_ty(&ty, &mut |ty| match ty {
+                ast::Type::RefType(r) => potential_lt_refs.push((
+                    pat.as_ref().and_then(|it| match it {
+                        ast::Pat::IdentPat(p) => p.name(),
+                        _ => None,
+                    }),
+                    r,
+                )),
+                _ => (),
+            })
+        });
+
+    enum LifetimeKind {
+        Elided,
+        Named(SmolStr),
+        Static,
+    }
+
+    let fetch_lt_text = |lt: Option<ast::Lifetime>| match lt {
+        Some(lt) => match lt.text().as_str() {
+            "'_" => LifetimeKind::Elided,
+            "'static" => LifetimeKind::Static,
+            name => LifetimeKind::Named(name.into()),
+        },
+        None => LifetimeKind::Elided,
+    };
+    let is_elided = |lt: Option<ast::Lifetime>| match lt {
+        Some(lt) => matches!(lt.text().as_str(), "'_"),
+        None => true,
+    };
+
+    // allocate names
+    if let Some(self_param) = &self_param {
+        if is_elided(self_param.lifetime()) {
+            allocated_lifetimes.push(if config.param_names_for_lifetime_elision_hints {
+                // self can't be used as a lifetime, so no need to check for collisions
+                "'self".into()
+            } else {
+                gen_idx_name()
+            });
+        }
+    }
+    potential_lt_refs.iter().for_each(|(name, it)| {
+        if is_elided(it.lifetime()) {
+            let name = match name {
+                Some(it) => {
+                    if let Some(c) = used_names.get_mut(it.text().as_str()) {
+                        *c += 1;
+                        SmolStr::from(format!("'{text}{c}", text = it.text().as_str()))
+                    } else {
+                        used_names.insert(it.text().as_str().into(), 0);
+                        SmolStr::from_iter(["\'", it.text().as_str()])
+                    }
+                }
+                _ => gen_idx_name(),
+            };
+            allocated_lifetimes.push(name);
+        }
+    });
+
+    // fetch output lifetime if elision rule applies
+
+    let output = if let Some(self_param) = &self_param {
+        match fetch_lt_text(self_param.lifetime()) {
+            LifetimeKind::Elided => allocated_lifetimes.get(0).cloned(),
+            LifetimeKind::Named(name) => Some(name),
+            LifetimeKind::Static => None,
+        }
+    } else {
+        match potential_lt_refs.as_slice() {
+            [(_, r)] => match fetch_lt_text(r.lifetime()) {
+                LifetimeKind::Elided => allocated_lifetimes.get(0).cloned(),
+                LifetimeKind::Named(name) => Some(name),
+                LifetimeKind::Static => None,
+            },
+            [..] => None,
+        }
+    };
+
+    if allocated_lifetimes.is_empty() && output.is_none() {
+        return None;
+    }
+
+    // apply hints
+    // apply output if required
+    let mut is_trivial = true;
+    if let (Some(output_lt), Some(r)) = (&output, ret_type) {
+        if let Some(ty) = r.ty() {
+            walk_ty(&ty, &mut |ty| match ty {
+                ast::Type::RefType(ty) if ty.lifetime().is_none() => {
+                    if let Some(amp) = ty.amp_token() {
+                        is_trivial = false;
+                        acc.push(InlayHint {
+                            range: amp.text_range(),
+                            kind: InlayKind::LifetimeHint,
+                            label: output_lt.clone(),
+                        });
+                    }
+                }
+                _ => (),
+            })
+        }
+    }
+
+    if config.lifetime_elision_hints == LifetimeElisionHints::SkipTrivial && is_trivial {
+        return None;
+    }
+
+    let mut idx = match &self_param {
+        Some(self_param) if is_elided(self_param.lifetime()) => {
+            if let Some(amp) = self_param.amp_token() {
+                let lt = allocated_lifetimes[0].clone();
+                acc.push(InlayHint {
+                    range: amp.text_range(),
+                    kind: InlayKind::LifetimeHint,
+                    label: lt,
+                });
+            }
+            1
+        }
+        _ => 0,
+    };
+
+    for (_, p) in potential_lt_refs.iter() {
+        if is_elided(p.lifetime()) {
+            let t = p.amp_token()?;
+            let lt = allocated_lifetimes[idx].clone();
+            acc.push(InlayHint { range: t.text_range(), kind: InlayKind::LifetimeHint, label: lt });
+            idx += 1;
+        }
+    }
+
+    // generate generic param list things
+    match (generic_param_list, allocated_lifetimes.as_slice()) {
+        (_, []) => (),
+        (Some(gpl), allocated_lifetimes) => {
+            let angle_tok = gpl.l_angle_token()?;
+            let is_empty = gpl.generic_params().next().is_none();
+            acc.push(InlayHint {
+                range: angle_tok.text_range(),
+                kind: InlayKind::GenericParamListHint,
+                label: format!(
+                    "{}{}",
+                    allocated_lifetimes.iter().format(", "),
+                    if is_empty { "" } else { ", " }
+                )
+                .into(),
+            });
+        }
+        (None, allocated_lifetimes) => acc.push(InlayHint {
+            range: func.name()?.syntax().text_range(),
+            kind: InlayKind::GenericParamListHint,
+            label: format!("<{}>", allocated_lifetimes.iter().format(", "),).into(),
+        }),
     }
+    Some(())
 }
 
 fn closure_ret_hints(
@@ -600,18 +820,21 @@ fn get_callable(
 mod tests {
     use expect_test::{expect, Expect};
     use ide_db::base_db::FileRange;
+    use itertools::Itertools;
     use syntax::{TextRange, TextSize};
     use test_utils::extract_annotations;
 
-    use crate::{fixture, inlay_hints::InlayHintsConfig};
+    use crate::{fixture, inlay_hints::InlayHintsConfig, LifetimeElisionHints};
 
     const DISABLED_CONFIG: InlayHintsConfig = InlayHintsConfig {
         render_colons: false,
         type_hints: false,
         parameter_hints: false,
         chaining_hints: false,
+        lifetime_elision_hints: LifetimeElisionHints::Never,
         hide_named_constructor_hints: false,
         closure_return_type_hints: false,
+        param_names_for_lifetime_elision_hints: false,
         max_length: None,
     };
     const TEST_CONFIG: InlayHintsConfig = InlayHintsConfig {
@@ -619,6 +842,7 @@ mod tests {
         parameter_hints: true,
         chaining_hints: true,
         closure_return_type_hints: true,
+        lifetime_elision_hints: LifetimeElisionHints::Always,
         ..DISABLED_CONFIG
     };
 
@@ -648,10 +872,15 @@ mod tests {
     #[track_caller]
     fn check_with_config(config: InlayHintsConfig, ra_fixture: &str) {
         let (analysis, file_id) = fixture::file(ra_fixture);
-        let expected = extract_annotations(&*analysis.file_text(file_id).unwrap());
+        let mut expected = extract_annotations(&*analysis.file_text(file_id).unwrap());
         let inlay_hints = analysis.inlay_hints(&config, file_id, None).unwrap();
-        let actual =
-            inlay_hints.into_iter().map(|it| (it.range, it.label.to_string())).collect::<Vec<_>>();
+        let actual = inlay_hints
+            .into_iter()
+            .map(|it| (it.range, it.label.to_string()))
+            .sorted_by_key(|(range, _)| range.start())
+            .collect::<Vec<_>>();
+        expected.sort_by_key(|(range, _)| range.start());
+
         assert_eq!(expected, actual, "\nExpected:\n{:#?}\n\nActual:\n{:#?}", expected, actual);
     }
 
@@ -1289,41 +1518,6 @@ fn main() {
         );
     }
 
-    #[test]
-    fn incomplete_for_no_hint() {
-        check_types(
-            r#"
-fn main() {
-    let data = &[1i32, 2, 3];
-      //^^^^ &[i32; 3]
-    for i
-}"#,
-        );
-        check(
-            r#"
-pub struct Vec<T> {}
-
-impl<T> Vec<T> {
-    pub fn new() -> Self { Vec {} }
-    pub fn push(&mut self, t: T) {}
-}
-
-impl<T> IntoIterator for Vec<T> {
-    type Item=T;
-}
-
-fn main() {
-    let mut data = Vec::new();
-          //^^^^ Vec<&str>
-    data.push("foo");
-    for i in
-
-    println!("Unit expr");
-}
-"#,
-        );
-    }
-
     #[test]
     fn complete_for_hint() {
         check_types(
@@ -1825,4 +2019,103 @@ fn main() {
             "#]],
         );
     }
+
+    #[test]
+    fn hints_lifetimes() {
+        check(
+            r#"
+fn empty() {}
+
+fn no_gpl(a: &()) {}
+ //^^^^^^<'0>
+          // ^'0
+fn empty_gpl<>(a: &()) {}
+      //    ^'0   ^'0
+fn partial<'b>(a: &(), b: &'b ()) {}
+//        ^'0, $  ^'0
+fn partial<'a>(a: &'a (), b: &()) {}
+//        ^'0, $             ^'0
+
+fn single_ret(a: &()) -> &() {}
+// ^^^^^^^^^^<'0>
+              // ^'0     ^'0
+fn full_mul(a: &(), b: &()) {}
+// ^^^^^^^^<'0, '1>
+            // ^'0     ^'1
+
+fn foo<'c>(a: &'c ()) -> &() {}
+                      // ^'c
+
+fn nested_in(a: &   &X< &()>) {}
+// ^^^^^^^^^<'0, '1, '2>
+              //^'0 ^'1 ^'2
+fn nested_out(a: &()) -> &   &X< &()>{}
+// ^^^^^^^^^^<'0>
+               //^'0     ^'0 ^'0 ^'0
+
+impl () {
+    fn foo(&self) {}
+    // ^^^<'0>
+        // ^'0
+    fn foo(&self) -> &() {}
+    // ^^^<'0>
+        // ^'0       ^'0
+    fn foo(&self, a: &()) -> &() {}
+    // ^^^<'0, '1>
+        // ^'0       ^'1     ^'0
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn hints_lifetimes_named() {
+        check_with_config(
+            InlayHintsConfig { param_names_for_lifetime_elision_hints: true, ..TEST_CONFIG },
+            r#"
+fn nested_in<'named>(named: &        &X<      &()>) {}
+//          ^'named1, 'named2, 'named3, $
+                          //^'named1 ^'named2 ^'named3
+"#,
+        );
+    }
+
+    #[test]
+    fn hints_lifetimes_trivial_skip() {
+        check_with_config(
+            InlayHintsConfig {
+                lifetime_elision_hints: LifetimeElisionHints::SkipTrivial,
+                ..TEST_CONFIG
+            },
+            r#"
+fn no_gpl(a: &()) {}
+fn empty_gpl<>(a: &()) {}
+fn partial<'b>(a: &(), b: &'b ()) {}
+fn partial<'a>(a: &'a (), b: &()) {}
+
+fn single_ret(a: &()) -> &() {}
+// ^^^^^^^^^^<'0>
+              // ^'0     ^'0
+fn full_mul(a: &(), b: &()) {}
+
+fn foo<'c>(a: &'c ()) -> &() {}
+                      // ^'c
+
+fn nested_in(a: &   &X< &()>) {}
+fn nested_out(a: &()) -> &   &X< &()>{}
+// ^^^^^^^^^^<'0>
+               //^'0     ^'0 ^'0 ^'0
+
+impl () {
+    fn foo(&self) {}
+    fn foo(&self) -> &() {}
+    // ^^^<'0>
+        // ^'0       ^'0
+    fn foo(&self, a: &()) -> &() {}
+    // ^^^<'0, '1>
+        // ^'0       ^'1     ^'0
+}
+"#,
+        );
+    }
 }
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index 20a5886c89..ff20d98f1f 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -81,7 +81,7 @@ pub use crate::{
     folding_ranges::{Fold, FoldKind},
     highlight_related::{HighlightRelatedConfig, HighlightedRange},
     hover::{HoverAction, HoverConfig, HoverDocFormat, HoverGotoTypeData, HoverResult},
-    inlay_hints::{InlayHint, InlayHintsConfig, InlayKind},
+    inlay_hints::{InlayHint, InlayHintsConfig, InlayKind, LifetimeElisionHints},
     join_lines::JoinLinesConfig,
     markup::Markup,
     moniker::{MonikerKind, MonikerResult, PackageInformation},
diff --git a/crates/ide/src/static_index.rs b/crates/ide/src/static_index.rs
index a06793cf51..3c81bfa3a9 100644
--- a/crates/ide/src/static_index.rs
+++ b/crates/ide/src/static_index.rs
@@ -12,11 +12,14 @@ use ide_db::{
 use rustc_hash::FxHashSet;
 use syntax::{AstNode, SyntaxKind::*, SyntaxToken, TextRange, T};
 
-use crate::moniker::{crate_for_file, def_to_moniker, MonikerResult};
 use crate::{
     hover::hover_for_definition, Analysis, Fold, HoverConfig, HoverDocFormat, HoverResult,
     InlayHint, InlayHintsConfig, TryToNav,
 };
+use crate::{
+    moniker::{crate_for_file, def_to_moniker, MonikerResult},
+    LifetimeElisionHints,
+};
 
 /// A static representation of fully analyzed source code.
 ///
@@ -110,7 +113,9 @@ impl StaticIndex<'_> {
                     parameter_hints: true,
                     chaining_hints: true,
                     closure_return_type_hints: true,
+                    lifetime_elision_hints: LifetimeElisionHints::Never,
                     hide_named_constructor_hints: false,
+                    param_names_for_lifetime_elision_hints: false,
                     max_length: Some(25),
                 },
                 file_id,
diff --git a/crates/ide_assists/src/handlers/convert_to_guarded_return.rs b/crates/ide_assists/src/handlers/convert_to_guarded_return.rs
index 445567b7f9..a7c43413d2 100644
--- a/crates/ide_assists/src/handlers/convert_to_guarded_return.rs
+++ b/crates/ide_assists/src/handlers/convert_to_guarded_return.rs
@@ -97,7 +97,7 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext)
     let parent_container = parent_block.syntax().parent()?;
 
     let early_expression: ast::Expr = match parent_container.kind() {
-        WHILE_EXPR | LOOP_EXPR => make::expr_continue(None),
+        WHILE_EXPR | LOOP_EXPR => make::expr_continue(),
         FN => make::expr_return(None),
         _ => return None,
     };
diff --git a/crates/ide_assists/src/handlers/convert_while_to_loop.rs b/crates/ide_assists/src/handlers/convert_while_to_loop.rs
index 739097067d..345d0c2063 100644
--- a/crates/ide_assists/src/handlers/convert_while_to_loop.rs
+++ b/crates/ide_assists/src/handlers/convert_while_to_loop.rs
@@ -53,7 +53,7 @@ pub(crate) fn convert_while_to_loop(acc: &mut Assists, ctx: &AssistContext) -> O
             let while_indent_level = IndentLevel::from_node(while_expr.syntax());
 
             let break_block =
-                make::block_expr(once(make::expr_stmt(make::expr_break(None, None)).into()), None)
+                make::block_expr(once(make::expr_stmt(make::expr_break(None)).into()), None)
                     .indent(while_indent_level);
             let block_expr = if is_pattern_cond(while_cond.clone()) {
                 let if_expr = make::expr_if(while_cond, while_body, Some(break_block.into()));
diff --git a/crates/ide_assists/src/handlers/extract_function.rs b/crates/ide_assists/src/handlers/extract_function.rs
index 8352774be4..e48a1cd9ef 100644
--- a/crates/ide_assists/src/handlers/extract_function.rs
+++ b/crates/ide_assists/src/handlers/extract_function.rs
@@ -287,10 +287,10 @@ enum FlowKind {
     Try {
         kind: TryKind,
     },
-    /// Break with label and value (`break 'label $expr;`)
-    Break(Option<ast::Lifetime>, Option<ast::Expr>),
-    /// Continue with label (`continue 'label;`)
-    Continue(Option<ast::Lifetime>),
+    /// Break with value (`break $expr;`)
+    Break(Option<ast::Expr>),
+    /// Continue
+    Continue,
 }
 
 #[derive(Debug, Clone)]
@@ -433,21 +433,21 @@ impl FlowKind {
     fn make_result_handler(&self, expr: Option<ast::Expr>) -> ast::Expr {
         match self {
             FlowKind::Return(_) => make::expr_return(expr),
-            FlowKind::Break(label, _) => make::expr_break(label.clone(), expr),
+            FlowKind::Break(_) => make::expr_break(expr),
             FlowKind::Try { .. } => {
                 stdx::never!("cannot have result handler with try");
                 expr.unwrap_or_else(|| make::expr_return(None))
             }
-            FlowKind::Continue(label) => {
+            FlowKind::Continue => {
                 stdx::always!(expr.is_none(), "continue with value is not possible");
-                make::expr_continue(label.clone())
+                make::expr_continue()
             }
         }
     }
 
     fn expr_ty(&self, ctx: &AssistContext) -> Option<hir::Type> {
         match self {
-            FlowKind::Return(Some(expr)) | FlowKind::Break(_, Some(expr)) => {
+            FlowKind::Return(Some(expr)) | FlowKind::Break(Some(expr)) => {
                 ctx.sema.type_of_expr(expr).map(TypeInfo::adjusted)
             }
             FlowKind::Try { .. } => {
@@ -839,8 +839,8 @@ impl FunctionBody {
                 cov_mark::hit!(external_control_flow_break_and_continue);
                 return None;
             }
-            (None, None, Some(b), None) => Some(FlowKind::Break(b.lifetime(), b.expr())),
-            (None, None, None, Some(c)) => Some(FlowKind::Continue(c.lifetime())),
+            (None, None, Some(b), None) => Some(FlowKind::Break(b.expr())),
+            (None, None, None, Some(_)) => Some(FlowKind::Continue),
             (None, None, None, None) => None,
         };
 
@@ -1185,20 +1185,20 @@ impl FlowHandler {
                 let action = flow_kind.clone();
                 if *ret_ty == FunType::Unit {
                     match flow_kind {
-                        FlowKind::Return(None)
-                        | FlowKind::Break(_, None)
-                        | FlowKind::Continue(_) => FlowHandler::If { action },
-                        FlowKind::Return(_) | FlowKind::Break(_, _) => {
+                        FlowKind::Return(None) | FlowKind::Break(None) | FlowKind::Continue => {
+                            FlowHandler::If { action }
+                        }
+                        FlowKind::Return(_) | FlowKind::Break(_) => {
                             FlowHandler::IfOption { action }
                         }
                         FlowKind::Try { kind } => FlowHandler::Try { kind: kind.clone() },
                     }
                 } else {
                     match flow_kind {
-                        FlowKind::Return(None)
-                        | FlowKind::Break(_, None)
-                        | FlowKind::Continue(_) => FlowHandler::MatchOption { none: action },
-                        FlowKind::Return(_) | FlowKind::Break(_, _) => {
+                        FlowKind::Return(None) | FlowKind::Break(None) | FlowKind::Continue => {
+                            FlowHandler::MatchOption { none: action }
+                        }
+                        FlowKind::Return(_) | FlowKind::Break(_) => {
                             FlowHandler::MatchResult { err: action }
                         }
                         FlowKind::Try { kind } => FlowHandler::Try { kind: kind.clone() },
@@ -3404,76 +3404,6 @@ fn $0fun_name(n: i32) -> ControlFlow<()> {
         );
     }
 
-    #[test]
-    fn break_loop_nested_labeled() {
-        check_assist(
-            extract_function,
-            r#"
-//- minicore: try
-fn foo() {
-    'bar: loop {
-        loop {
-            $0break 'bar;$0
-        }
-    }
-}
-"#,
-            r#"
-use core::ops::ControlFlow;
-
-fn foo() {
-    'bar: loop {
-        loop {
-            if let ControlFlow::Break(_) = fun_name() {
-                break 'bar;
-            }
-        }
-    }
-}
-
-fn $0fun_name() -> ControlFlow<()> {
-    return ControlFlow::Break(());
-    ControlFlow::Continue(())
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn continue_loop_nested_labeled() {
-        check_assist(
-            extract_function,
-            r#"
-//- minicore: try
-fn foo() {
-    'bar: loop {
-        loop {
-            $0continue 'bar;$0
-        }
-    }
-}
-"#,
-            r#"
-use core::ops::ControlFlow;
-
-fn foo() {
-    'bar: loop {
-        loop {
-            if let ControlFlow::Break(_) = fun_name() {
-                continue 'bar;
-            }
-        }
-    }
-}
-
-fn $0fun_name() -> ControlFlow<()> {
-    return ControlFlow::Break(());
-    ControlFlow::Continue(())
-}
-"#,
-        );
-    }
-
     #[test]
     fn return_from_nested_loop() {
         check_assist(
@@ -3678,46 +3608,6 @@ fn $0fun_name() -> Option<i32> {
         );
     }
 
-    #[test]
-    fn break_with_value_and_label() {
-        check_assist(
-            extract_function,
-            r#"
-fn foo() -> i32 {
-    'bar: loop {
-        let n = 1;
-        $0let k = 1;
-        if k == 42 {
-            break 'bar 4;
-        }
-        let m = k + 1;$0
-        let h = 1;
-    }
-}
-"#,
-            r#"
-fn foo() -> i32 {
-    'bar: loop {
-        let n = 1;
-        if let Some(value) = fun_name() {
-            break 'bar value;
-        }
-        let h = 1;
-    }
-}
-
-fn $0fun_name() -> Option<i32> {
-    let k = 1;
-    if k == 42 {
-        return Some(4);
-    }
-    let m = k + 1;
-    None
-}
-"#,
-        );
-    }
-
     #[test]
     fn break_with_value_and_return() {
         check_assist(
diff --git a/crates/ide_assists/src/handlers/generate_enum_is_method.rs b/crates/ide_assists/src/handlers/generate_enum_is_method.rs
index 863ada79ba..c2a6e438b1 100644
--- a/crates/ide_assists/src/handlers/generate_enum_is_method.rs
+++ b/crates/ide_assists/src/handlers/generate_enum_is_method.rs
@@ -30,7 +30,6 @@ use crate::{
 //     /// Returns `true` if the version is [`Minor`].
 //     ///
 //     /// [`Minor`]: Version::Minor
-//     #[must_use]
 //     fn is_minor(&self) -> bool {
 //         matches!(self, Self::Minor)
 //     }
@@ -64,7 +63,6 @@ pub(crate) fn generate_enum_is_method(acc: &mut Assists, ctx: &AssistContext) ->
                 "    /// Returns `true` if the {} is [`{variant}`].
     ///
     /// [`{variant}`]: {}::{variant}
-    #[must_use]
     {}fn {}(&self) -> bool {{
         matches!(self, Self::{variant}{})
     }}",
@@ -107,7 +105,6 @@ impl Variant {
     /// Returns `true` if the variant is [`Minor`].
     ///
     /// [`Minor`]: Variant::Minor
-    #[must_use]
     fn is_minor(&self) -> bool {
         matches!(self, Self::Minor)
     }
@@ -154,7 +151,6 @@ impl Variant {
     /// Returns `true` if the variant is [`Minor`].
     ///
     /// [`Minor`]: Variant::Minor
-    #[must_use]
     fn is_minor(&self) -> bool {
         matches!(self, Self::Minor(..))
     }
@@ -182,7 +178,6 @@ impl Variant {
     /// Returns `true` if the variant is [`Minor`].
     ///
     /// [`Minor`]: Variant::Minor
-    #[must_use]
     fn is_minor(&self) -> bool {
         matches!(self, Self::Minor { .. })
     }
@@ -202,7 +197,6 @@ impl Variant {
     /// Returns `true` if the variant is [`Undefined`].
     ///
     /// [`Undefined`]: Variant::Undefined
-    #[must_use]
     fn is_undefined(&self) -> bool {
         matches!(self, Self::Undefined)
     }
@@ -230,7 +224,6 @@ impl Variant {
     /// Returns `true` if the variant is [`Minor`].
     ///
     /// [`Minor`]: Variant::Minor
-    #[must_use]
     pub(crate) fn is_minor(&self) -> bool {
         matches!(self, Self::Minor)
     }
@@ -253,7 +246,6 @@ impl Variant {
     /// Returns `true` if the variant is [`Minor`].
     ///
     /// [`Minor`]: Variant::Minor
-    #[must_use]
     fn is_minor(&self) -> bool {
         matches!(self, Self::Minor)
     }
@@ -268,7 +260,6 @@ impl Variant {
     /// Returns `true` if the variant is [`Minor`].
     ///
     /// [`Minor`]: Variant::Minor
-    #[must_use]
     fn is_minor(&self) -> bool {
         matches!(self, Self::Minor)
     }
@@ -276,7 +267,6 @@ impl Variant {
     /// Returns `true` if the variant is [`Major`].
     ///
     /// [`Major`]: Variant::Major
-    #[must_use]
     fn is_major(&self) -> bool {
         matches!(self, Self::Major)
     }
@@ -304,7 +294,6 @@ impl GeneratorState {
     /// Returns `true` if the generator state is [`Complete`].
     ///
     /// [`Complete`]: GeneratorState::Complete
-    #[must_use]
     fn is_complete(&self) -> bool {
         matches!(self, Self::Complete)
     }
diff --git a/crates/ide_assists/src/handlers/generate_getter.rs b/crates/ide_assists/src/handlers/generate_getter.rs
index 2e81dd13f4..c6e979c072 100644
--- a/crates/ide_assists/src/handlers/generate_getter.rs
+++ b/crates/ide_assists/src/handlers/generate_getter.rs
@@ -39,7 +39,6 @@ use crate::{
 //
 // impl Person {
 //     /// Get a reference to the person's name.
-//     #[must_use]
 //     fn $0name(&self) -> &str {
 //         self.name.as_ref()
 //     }
@@ -66,7 +65,6 @@ pub(crate) fn generate_getter(acc: &mut Assists, ctx: &AssistContext) -> Option<
 //
 // impl Person {
 //     /// Get a mutable reference to the person's name.
-//     #[must_use]
 //     fn $0name_mut(&mut self) -> &mut String {
 //         &mut self.name
 //     }
@@ -145,7 +143,6 @@ pub(crate) fn generate_getter_impl(
             format_to!(
                 buf,
                 "    /// Get {}the {}'s {}.
-    #[must_use]
     {}fn {}(&{}self) -> {} {{
         {}
     }}",
@@ -198,7 +195,6 @@ struct Context {
 
 impl Context {
     /// Get a reference to the context's data.
-    #[must_use]
     fn $0data(&self) -> &Data {
         &self.data
     }
@@ -220,7 +216,6 @@ struct Context {
 
 impl Context {
     /// Get a mutable reference to the context's data.
-    #[must_use]
     fn $0data_mut(&mut self) -> &mut Data {
         &mut self.data
     }
@@ -254,7 +249,6 @@ struct Context {
 }
 
 impl Context {
-    #[must_use]
     fn data_mut(&mut self) -> &mut Data {
         &mut self.data
     }
@@ -279,7 +273,6 @@ pub(crate) struct Context {
 
 impl Context {
     /// Get a reference to the context's data.
-    #[must_use]
     pub(crate) fn $0data(&self) -> &Data {
         &self.data
     }
@@ -300,7 +293,6 @@ struct Context {
 
 impl Context {
     /// Get a reference to the context's data.
-    #[must_use]
     fn data(&self) -> &Data {
         &self.data
     }
@@ -314,13 +306,11 @@ struct Context {
 
 impl Context {
     /// Get a reference to the context's data.
-    #[must_use]
     fn data(&self) -> &Data {
         &self.data
     }
 
     /// Get a reference to the context's count.
-    #[must_use]
     fn $0count(&self) -> &usize {
         &self.count
     }
@@ -347,7 +337,6 @@ struct S { foo: String }
 
 impl S {
     /// Get a reference to the s's foo.
-    #[must_use]
     fn $0foo(&self) -> &String {
         &self.foo
     }
@@ -372,7 +361,6 @@ struct S { foo: bool }
 
 impl S {
     /// Get the s's foo.
-    #[must_use]
     fn $0foo(&self) -> bool {
         self.foo
     }
@@ -406,7 +394,6 @@ struct S { foo: String }
 
 impl S {
     /// Get a reference to the s's foo.
-    #[must_use]
     fn $0foo(&self) -> &str {
         self.foo.as_ref()
     }
@@ -444,7 +431,6 @@ struct S { foo: Box<Sweets> }
 
 impl S {
     /// Get a reference to the s's foo.
-    #[must_use]
     fn $0foo(&self) -> &Sweets {
         self.foo.as_ref()
     }
@@ -478,7 +464,6 @@ struct S { foo: Vec<()> }
 
 impl S {
     /// Get a reference to the s's foo.
-    #[must_use]
     fn $0foo(&self) -> &[()] {
         self.foo.as_ref()
     }
@@ -502,7 +487,6 @@ struct S { foo: Option<Failure> }
 
 impl S {
     /// Get a reference to the s's foo.
-    #[must_use]
     fn $0foo(&self) -> Option<&Failure> {
         self.foo.as_ref()
     }
@@ -526,7 +510,6 @@ struct Context {
 
 impl Context {
     /// Get a reference to the context's data.
-    #[must_use]
     fn $0data(&self) -> Result<&bool, &i32> {
         self.data.as_ref()
     }
diff --git a/crates/ide_assists/src/handlers/generate_is_empty_from_len.rs b/crates/ide_assists/src/handlers/generate_is_empty_from_len.rs
index bd7c85c443..db0cfbe2ec 100644
--- a/crates/ide_assists/src/handlers/generate_is_empty_from_len.rs
+++ b/crates/ide_assists/src/handlers/generate_is_empty_from_len.rs
@@ -17,7 +17,6 @@ use crate::{
 // struct MyStruct { data: Vec<String> }
 //
 // impl MyStruct {
-//     #[must_use]
 //     p$0ub fn len(&self) -> usize {
 //         self.data.len()
 //     }
@@ -28,12 +27,10 @@ use crate::{
 // struct MyStruct { data: Vec<String> }
 //
 // impl MyStruct {
-//     #[must_use]
 //     pub fn len(&self) -> usize {
 //         self.data.len()
 //     }
 //
-//     #[must_use]
 //     pub fn is_empty(&self) -> bool {
 //         self.len() == 0
 //     }
@@ -75,7 +72,6 @@ pub(crate) fn generate_is_empty_from_len(acc: &mut Assists, ctx: &AssistContext)
         |builder| {
             let code = r#"
 
-    #[must_use]
     pub fn is_empty(&self) -> bool {
         self.len() == 0
     }"#
@@ -133,7 +129,6 @@ impl MyStruct {
 struct MyStruct { data: Vec<String> }
 
 impl MyStruct {
-    #[must_use]
     p$0ub fn len(&self, _i: bool) -> usize {
         self.data.len()
     }
@@ -151,12 +146,10 @@ impl MyStruct {
 struct MyStruct { data: Vec<String> }
 
 impl MyStruct {
-    #[must_use]
     p$0ub fn len(&self) -> usize {
         self.data.len()
     }
 
-    #[must_use]
     pub fn is_empty(&self) -> bool {
         self.len() == 0
     }
@@ -174,7 +167,6 @@ impl MyStruct {
 struct MyStruct { data: Vec<String> }
 
 impl MyStruct {
-    #[must_use]
     p$0ub fn len(&self) -> u32 {
         self.data.len()
     }
@@ -191,7 +183,6 @@ impl MyStruct {
 struct MyStruct { data: Vec<String> }
 
 impl MyStruct {
-    #[must_use]
     p$0ub fn len(&self) -> usize {
         self.data.len()
     }
@@ -201,12 +192,10 @@ impl MyStruct {
 struct MyStruct { data: Vec<String> }
 
 impl MyStruct {
-    #[must_use]
     pub fn len(&self) -> usize {
         self.data.len()
     }
 
-    #[must_use]
     pub fn is_empty(&self) -> bool {
         self.len() == 0
     }
@@ -223,12 +212,10 @@ impl MyStruct {
 struct MyStruct { data: Vec<String> }
 
 impl MyStruct {
-    #[must_use]
     pub fn new() -> Self {
         Self { data: 0 }
     }
 
-    #[must_use]
     p$0ub fn len(&self) -> usize {
         self.data.len()
     }
@@ -242,17 +229,14 @@ impl MyStruct {
 struct MyStruct { data: Vec<String> }
 
 impl MyStruct {
-    #[must_use]
     pub fn new() -> Self {
         Self { data: 0 }
     }
 
-    #[must_use]
     pub fn len(&self) -> usize {
         self.data.len()
     }
 
-    #[must_use]
     pub fn is_empty(&self) -> bool {
         self.len() == 0
     }
@@ -273,14 +257,12 @@ impl MyStruct {
 struct MyStruct { data: Vec<String> }
 
 impl MyStruct {
-    #[must_use]
     p$0ub fn len(&self) -> usize {
         self.data.len()
     }
 }
 
 impl MyStruct {
-    #[must_use]
     pub fn is_empty(&self) -> bool {
         self.len() == 0
     }
diff --git a/crates/ide_assists/src/handlers/generate_new.rs b/crates/ide_assists/src/handlers/generate_new.rs
index eb9f1d04e1..6a1f710f6d 100644
--- a/crates/ide_assists/src/handlers/generate_new.rs
+++ b/crates/ide_assists/src/handlers/generate_new.rs
@@ -23,7 +23,6 @@ use crate::{
 // }
 //
 // impl<T: Clone> Ctx<T> {
-//     #[must_use]
 //     fn $0new(data: T) -> Self { Self { data } }
 // }
 // ```
@@ -55,13 +54,7 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
             .format(", ");
         let fields = field_list.fields().filter_map(|f| f.name()).format(", ");
 
-        format_to!(
-            buf,
-            "    #[must_use]\n    {}fn new({}) -> Self {{ Self {{ {} }} }}",
-            vis,
-            params,
-            fields
-        );
+        format_to!(buf, "    {}fn new({}) -> Self {{ Self {{ {} }} }}", vis, params, fields);
 
         let start_offset = impl_def
             .and_then(|impl_def| find_impl_block_start(impl_def, &mut buf))
@@ -97,7 +90,6 @@ struct Foo {$0}
 struct Foo {}
 
 impl Foo {
-    #[must_use]
     fn $0new() -> Self { Self {  } }
 }
 "#,
@@ -111,7 +103,6 @@ struct Foo<T: Clone> {$0}
 struct Foo<T: Clone> {}
 
 impl<T: Clone> Foo<T> {
-    #[must_use]
     fn $0new() -> Self { Self {  } }
 }
 "#,
@@ -125,7 +116,6 @@ struct Foo<'a, T: Foo<'a>> {$0}
 struct Foo<'a, T: Foo<'a>> {}
 
 impl<'a, T: Foo<'a>> Foo<'a, T> {
-    #[must_use]
     fn $0new() -> Self { Self {  } }
 }
 "#,
@@ -139,7 +129,6 @@ struct Foo { baz: String $0}
 struct Foo { baz: String }
 
 impl Foo {
-    #[must_use]
     fn $0new(baz: String) -> Self { Self { baz } }
 }
 "#,
@@ -153,7 +142,6 @@ struct Foo { baz: String, qux: Vec<i32> $0}
 struct Foo { baz: String, qux: Vec<i32> }
 
 impl Foo {
-    #[must_use]
     fn $0new(baz: String, qux: Vec<i32>) -> Self { Self { baz, qux } }
 }
 "#,
@@ -171,7 +159,6 @@ struct Foo { pub baz: String, pub qux: Vec<i32> $0}
 struct Foo { pub baz: String, pub qux: Vec<i32> }
 
 impl Foo {
-    #[must_use]
     fn $0new(baz: String, qux: Vec<i32>) -> Self { Self { baz, qux } }
 }
 "#,
@@ -191,7 +178,6 @@ impl Foo {}
 struct Foo {}
 
 impl Foo {
-    #[must_use]
     fn $0new() -> Self { Self {  } }
 }
 "#,
@@ -209,7 +195,6 @@ impl Foo {
 struct Foo {}
 
 impl Foo {
-    #[must_use]
     fn $0new() -> Self { Self {  } }
 
     fn qux(&self) {}
@@ -233,7 +218,6 @@ impl Foo {
 struct Foo {}
 
 impl Foo {
-    #[must_use]
     fn $0new() -> Self { Self {  } }
 
     fn qux(&self) {}
@@ -256,7 +240,6 @@ pub struct Foo {$0}
 pub struct Foo {}
 
 impl Foo {
-    #[must_use]
     pub fn $0new() -> Self { Self {  } }
 }
 "#,
@@ -270,7 +253,6 @@ pub(crate) struct Foo {$0}
 pub(crate) struct Foo {}
 
 impl Foo {
-    #[must_use]
     pub(crate) fn $0new() -> Self { Self {  } }
 }
 "#,
@@ -366,7 +348,6 @@ pub struct Source<T> {
 }
 
 impl<T> Source<T> {
-    #[must_use]
     pub fn $0new(file_id: HirFileId, ast: T) -> Self { Self { file_id, ast } }
 
     pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> {
diff --git a/crates/ide_completion/src/completions/pattern.rs b/crates/ide_completion/src/completions/pattern.rs
index 0d3a49c668..6c17da07d6 100644
--- a/crates/ide_completion/src/completions/pattern.rs
+++ b/crates/ide_completion/src/completions/pattern.rs
@@ -137,38 +137,25 @@ fn pattern_path_completion(
                         }
                     }
                 }
-                res @ (hir::PathResolution::TypeParam(_)
-                | hir::PathResolution::SelfType(_)
-                | hir::PathResolution::Def(hir::ModuleDef::Adt(hir::Adt::Struct(_)))
-                | hir::PathResolution::Def(hir::ModuleDef::Adt(hir::Adt::Enum(_)))
-                | hir::PathResolution::Def(hir::ModuleDef::Adt(hir::Adt::Union(_)))
-                | hir::PathResolution::Def(hir::ModuleDef::BuiltinType(_))) => {
+                hir::PathResolution::Def(hir::ModuleDef::Adt(hir::Adt::Enum(e))) => {
+                    cov_mark::hit!(enum_plain_qualified_use_tree);
+                    e.variants(ctx.db)
+                        .into_iter()
+                        .for_each(|variant| acc.add_enum_variant(ctx, variant, None));
+                }
+                res @ (hir::PathResolution::TypeParam(_) | hir::PathResolution::SelfType(_)) => {
                     let ty = match res {
                         hir::PathResolution::TypeParam(param) => param.ty(ctx.db),
                         hir::PathResolution::SelfType(impl_def) => impl_def.self_ty(ctx.db),
-                        hir::PathResolution::Def(hir::ModuleDef::Adt(hir::Adt::Struct(s))) => {
-                            s.ty(ctx.db)
-                        }
-                        hir::PathResolution::Def(hir::ModuleDef::Adt(hir::Adt::Enum(e))) => {
-                            cov_mark::hit!(enum_plain_qualified_use_tree);
-                            e.variants(ctx.db)
-                                .into_iter()
-                                .for_each(|variant| acc.add_enum_variant(ctx, variant, None));
-                            e.ty(ctx.db)
-                        }
-                        hir::PathResolution::Def(hir::ModuleDef::Adt(hir::Adt::Union(u))) => {
-                            u.ty(ctx.db)
-                        }
-                        hir::PathResolution::Def(hir::ModuleDef::BuiltinType(ty)) => {
-                            let module = match ctx.module {
-                                Some(m) => m,
-                                None => return,
-                            };
-                            ty.ty(ctx.db, module)
-                        }
                         _ => return,
                     };
 
+                    if let Some(hir::Adt::Enum(e)) = ty.as_adt() {
+                        e.variants(ctx.db)
+                            .into_iter()
+                            .for_each(|variant| acc.add_enum_variant(ctx, variant, None));
+                    }
+
                     let traits_in_scope = ctx.scope.visible_traits();
                     let mut seen = FxHashSet::default();
                     ty.iterate_path_candidates(
@@ -178,19 +165,12 @@ fn pattern_path_completion(
                         ctx.module,
                         None,
                         |item| {
-                            match item {
-                                AssocItem::TypeAlias(ta) => {
-                                    // We might iterate candidates of a trait multiple times here, so deduplicate them.
-                                    if seen.insert(item) {
-                                        acc.add_type_alias(ctx, ta);
-                                    }
-                                }
-                                AssocItem::Const(c) => {
-                                    if seen.insert(item) {
-                                        acc.add_const(ctx, c);
-                                    }
+                            // Note associated consts cannot be referenced in patterns
+                            if let AssocItem::TypeAlias(ta) = item {
+                                // We might iterate candidates of a trait multiple times here, so deduplicate them.
+                                if seen.insert(item) {
+                                    acc.add_type_alias(ctx, ta);
                                 }
-                                _ => {}
                             }
                             None::<()>
                         },
diff --git a/crates/ide_db/Cargo.toml b/crates/ide_db/Cargo.toml
index 26b51739cf..c71a213c40 100644
--- a/crates/ide_db/Cargo.toml
+++ b/crates/ide_db/Cargo.toml
@@ -35,5 +35,5 @@ limit = { path = "../limit", version = "0.0.0" }
 [dev-dependencies]
 test_utils = { path = "../test_utils" }
 sourcegen = { path = "../sourcegen" }
-xshell = "0.2.1"
+xshell = "0.2.0"
 expect-test = "1.2.0-pre.1"
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml
index 079e56c9fa..68d728b41f 100644
--- a/crates/rust-analyzer/Cargo.toml
+++ b/crates/rust-analyzer/Cargo.toml
@@ -75,7 +75,7 @@ jemallocator = { version = "0.4.1", package = "tikv-jemallocator", optional = tr
 [dev-dependencies]
 expect-test = "1.2.0-pre.1"
 jod-thread = "0.1.0"
-xshell = "0.2.1"
+xshell = "0.2.0"
 
 test_utils = { path = "../test_utils" }
 sourcegen = { path = "../sourcegen" }
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 3d5737316b..3afbeac47c 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -12,7 +12,8 @@ use std::{ffi::OsString, iter, path::PathBuf};
 use flycheck::FlycheckConfig;
 use ide::{
     AssistConfig, CompletionConfig, DiagnosticsConfig, ExprFillDefaultMode, HighlightRelatedConfig,
-    HoverConfig, HoverDocFormat, InlayHintsConfig, JoinLinesConfig, Snippet, SnippetScope,
+    HoverConfig, HoverDocFormat, InlayHintsConfig, JoinLinesConfig, LifetimeElisionHints, Snippet,
+    SnippetScope,
 };
 use ide_db::{
     imports::insert_use::{ImportGranularity, InsertUseConfig, PrefixKind},
@@ -243,20 +244,24 @@ config_data! {
         hoverActions_run: bool             = "true",
 
         /// Whether to render trailing colons for parameter hints, and trailing colons for parameter hints.
-        inlayHints_renderColons: bool               = "true",
+        inlayHints_renderColons: bool                      = "true",
         /// Maximum length for inlay hints. Set to null to have an unlimited length.
-        inlayHints_maxLength: Option<usize>         = "25",
+        inlayHints_maxLength: Option<usize>                = "25",
         /// Whether to show function parameter name inlay hints at the call
         /// site.
-        inlayHints_parameterHints: bool             = "true",
+        inlayHints_parameterHints: bool                     = "true",
         /// Whether to show inlay type hints for variables.
-        inlayHints_typeHints: bool                  = "true",
+        inlayHints_typeHints: bool                          = "true",
         /// Whether to show inlay type hints for method chains.
-        inlayHints_chainingHints: bool              = "true",
+        inlayHints_chainingHints: bool                      = "true",
         /// Whether to show inlay type hints for return types of closures with blocks.
-        inlayHints_closureReturnTypeHints: bool     = "false",
+        inlayHints_closureReturnTypeHints: bool             = "false",
+        /// Whether to show inlay type hints for elided lifetimes in function signatures.
+        inlayHints_lifetimeElisionHints: 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.
-        inlayHints_hideNamedConstructorHints: bool  = "false",
+        inlayHints_hideNamedConstructorHints: bool          = "false",
 
         /// Join lines inserts else between consecutive ifs.
         joinLines_joinElseIf: bool = "true",
@@ -855,7 +860,15 @@ 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 {
+                LifetimeElisionDef::Always => LifetimeElisionHints::Always,
+                LifetimeElisionDef::Never => LifetimeElisionHints::Never,
+                LifetimeElisionDef::SkipTrivial => LifetimeElisionHints::SkipTrivial,
+            },
             hide_named_constructor_hints: self.data.inlayHints_hideNamedConstructorHints,
+            param_names_for_lifetime_elision_hints: self
+                .data
+                .inlayHints_lifetimeElisionHints_useParameterNames,
             max_length: self.data.inlayHints_maxLength,
         }
     }
@@ -1125,6 +1138,16 @@ enum ImportGranularityDef {
     Module,
 }
 
+#[derive(Deserialize, Debug, Clone)]
+#[serde(rename_all = "snake_case")]
+enum LifetimeElisionDef {
+    #[serde(alias = "true")]
+    Always,
+    #[serde(alias = "false")]
+    Never,
+    SkipTrivial,
+}
+
 #[derive(Deserialize, Debug, Clone)]
 #[serde(rename_all = "snake_case")]
 enum ImportPrefixDef {
@@ -1377,7 +1400,16 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json
             "minimum": 0,
             "maximum": 255
         },
-        _ => panic!("{}: {}", ty, default),
+        "LifetimeElisionDef" => set! {
+            "type": "string",
+            "enum": ["always", "never", "skip_trivial"],
+            "enumDescriptions": [
+                "Always show lifetime elision hints.",
+                "Never show lifetime elision hints.",
+                "Only show lifetime elision hints if a return type is involved."
+            ],
+        },
+        _ => panic!("missing entry for {}: {}", ty, default),
     }
 
     map.into()
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index e9e8bdb6c5..976542a491 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -427,27 +427,34 @@ pub(crate) fn inlay_hint(
         }),
         position: match inlay_hint.kind {
             InlayKind::ParameterHint => position(line_index, inlay_hint.range.start()),
-            InlayKind::ClosureReturnTypeHint | InlayKind::TypeHint | InlayKind::ChainingHint => {
-                position(line_index, inlay_hint.range.end())
-            }
+            InlayKind::ClosureReturnTypeHint
+            | InlayKind::TypeHint
+            | InlayKind::ChainingHint
+            | InlayKind::GenericParamListHint
+            | InlayKind::LifetimeHint => position(line_index, inlay_hint.range.end()),
         },
         kind: match inlay_hint.kind {
             InlayKind::ParameterHint => Some(lsp_ext::InlayHintKind::PARAMETER),
             InlayKind::ClosureReturnTypeHint | InlayKind::TypeHint | InlayKind::ChainingHint => {
                 Some(lsp_ext::InlayHintKind::TYPE)
             }
+            InlayKind::GenericParamListHint | InlayKind::LifetimeHint => None,
         },
         tooltip: None,
         padding_left: Some(match inlay_hint.kind {
             InlayKind::TypeHint => !render_colons,
             InlayKind::ParameterHint | InlayKind::ClosureReturnTypeHint => false,
             InlayKind::ChainingHint => true,
+            InlayKind::GenericParamListHint => false,
+            InlayKind::LifetimeHint => false,
         }),
         padding_right: Some(match inlay_hint.kind {
             InlayKind::TypeHint | InlayKind::ChainingHint | InlayKind::ClosureReturnTypeHint => {
                 false
             }
             InlayKind::ParameterHint => true,
+            InlayKind::LifetimeHint => true,
+            InlayKind::GenericParamListHint => false,
         }),
     }
 }
diff --git a/crates/sourcegen/Cargo.toml b/crates/sourcegen/Cargo.toml
index 57e183af3f..10587d913d 100644
--- a/crates/sourcegen/Cargo.toml
+++ b/crates/sourcegen/Cargo.toml
@@ -10,4 +10,4 @@ rust-version = "1.57"
 doctest = false
 
 [dependencies]
-xshell = "0.2.1"
+xshell = "0.2.0"
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs
index d11d658a27..a69a77c7ee 100644
--- a/crates/syntax/src/ast/make.rs
+++ b/crates/syntax/src/ast/make.rs
@@ -368,28 +368,18 @@ pub fn expr_empty_block() -> ast::Expr {
 pub fn expr_path(path: ast::Path) -> ast::Expr {
     expr_from_text(&path.to_string())
 }
-pub fn expr_continue(label: Option<ast::Lifetime>) -> ast::Expr {
-    match label {
-        Some(label) => expr_from_text(&format!("continue {}", label)),
-        None => expr_from_text("continue"),
-    }
+pub fn expr_continue() -> ast::Expr {
+    expr_from_text("continue")
 }
 // Consider `op: SyntaxKind` instead for nicer syntax at the call-site?
 pub fn expr_bin_op(lhs: ast::Expr, op: ast::BinaryOp, rhs: ast::Expr) -> ast::Expr {
     expr_from_text(&format!("{} {} {}", lhs, op, rhs))
 }
-pub fn expr_break(label: Option<ast::Lifetime>, expr: Option<ast::Expr>) -> ast::Expr {
-    let mut s = String::from("break");
-
-    if let Some(label) = label {
-        format_to!(s, " {}", label);
-    }
-
-    if let Some(expr) = expr {
-        format_to!(s, " {}", expr);
+pub fn expr_break(expr: Option<ast::Expr>) -> ast::Expr {
+    match expr {
+        Some(expr) => expr_from_text(&format!("break {}", expr)),
+        None => expr_from_text("break"),
     }
-
-    expr_from_text(&s)
 }
 pub fn expr_return(expr: Option<ast::Expr>) -> ast::Expr {
     match expr {
diff --git a/crates/vfs-notify/Cargo.toml b/crates/vfs-notify/Cargo.toml
index 10addab1e4..a670852d63 100644
--- a/crates/vfs-notify/Cargo.toml
+++ b/crates/vfs-notify/Cargo.toml
@@ -14,7 +14,7 @@ tracing = "0.1"
 jod-thread = "0.1.0"
 walkdir = "2.3.1"
 crossbeam-channel = "0.5.0"
-notify = "=5.0.0-pre.14"
+notify = "=5.0.0-pre.13"
 
 vfs = { path = "../vfs", version = "0.0.0" }
 paths = { path = "../paths", version = "0.0.0" }
diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc
index 5793b4c057..42f485b511 100644
--- a/docs/user/generated_config.adoc
+++ b/docs/user/generated_config.adoc
@@ -378,6 +378,16 @@ Whether to show inlay type hints for method chains.
 --
 Whether to show inlay type hints for return types of closures with blocks.
 --
+[[rust-analyzer.inlayHints.lifetimeElisionHints]]rust-analyzer.inlayHints.lifetimeElisionHints (default: `"never"`)::
++
+--
+Whether to show inlay type hints for elided lifetimes in function signatures.
+--
+[[rust-analyzer.inlayHints.lifetimeElisionHints.useParameterNames]]rust-analyzer.inlayHints.lifetimeElisionHints.useParameterNames (default: `false`)::
++
+--
+Whether to prefer using parameter names as the name for elided lifetime hints if possible.
+--
 [[rust-analyzer.inlayHints.hideNamedConstructorHints]]rust-analyzer.inlayHints.hideNamedConstructorHints (default: `false`)::
 +
 --
diff --git a/editors/code/package.json b/editors/code/package.json
index 56d10f587d..0343abd0a3 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -184,6 +184,11 @@
                 "title": "Restart server",
                 "category": "Rust Analyzer"
             },
+            {
+                "command": "rust-analyzer.updateGithubToken",
+                "title": "Update Github API token",
+                "category": "Rust Analyzer"
+            },
             {
                 "command": "rust-analyzer.onEnter",
                 "title": "Enhanced enter key",
@@ -795,6 +800,26 @@
                     "default": false,
                     "type": "boolean"
                 },
+                "rust-analyzer.inlayHints.lifetimeElisionHints": {
+                    "markdownDescription": "Whether to show inlay type hints for elided lifetimes in function signatures.",
+                    "default": "never",
+                    "type": "string",
+                    "enum": [
+                        "always",
+                        "never",
+                        "skip_trivial"
+                    ],
+                    "enumDescriptions": [
+                        "Always show lifetime elision hints.",
+                        "Never show lifetime elision hints.",
+                        "Only show lifetime elision hints if a return type is involved."
+                    ]
+                },
+                "rust-analyzer.inlayHints.lifetimeElisionHints.useParameterNames": {
+                    "markdownDescription": "Whether to prefer using parameter names as the name for elided lifetime hints if possible.",
+                    "default": false,
+                    "type": "boolean"
+                },
                 "rust-analyzer.inlayHints.hideNamedConstructorHints": {
                     "markdownDescription": "Whether to hide inlay hints for constructors.",
                     "default": false,
@@ -1427,6 +1452,10 @@
                     "command": "rust-analyzer.reload",
                     "when": "inRustProject"
                 },
+                {
+                    "command": "rust-analyzer.updateGithubToken",
+                    "when": "inRustProject"
+                },
                 {
                     "command": "rust-analyzer.onEnter",
                     "when": "inRustProject"
diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml
index de6a102427..d9adf88c02 100644
--- a/xtask/Cargo.toml
+++ b/xtask/Cargo.toml
@@ -10,6 +10,6 @@ rust-version = "1.57"
 anyhow = "1.0.26"
 flate2 = "1.0"
 write-json = "0.1.0"
-xshell = "0.2.1"
+xshell = "0.2.0"
 xflags = "0.2.1"
 # Avoid adding more dependencies to this crate
diff --git a/xtask/src/metrics.rs b/xtask/src/metrics.rs
index 3da7419582..aa700d715d 100644
--- a/xtask/src/metrics.rs
+++ b/xtask/src/metrics.rs
@@ -17,7 +17,7 @@ impl flags::Metrics {
     pub(crate) fn run(self, sh: &Shell) -> anyhow::Result<()> {
         let mut metrics = Metrics::new(sh)?;
         if !self.dry_run {
-            sh.remove_path("./target/release")?;
+            let _ = sh.remove_path("./target/release");
         }
         if !Path::new("./target/rustc-perf").exists() {
             sh.create_dir("./target/rustc-perf")?;
@@ -51,14 +51,10 @@ impl flags::Metrics {
                 "git clone --depth 1 https://{metrics_token}@github.com/rust-analyzer/metrics.git"
             )
             .run()?;
-
-            {
-                let mut file =
-                    fs::File::options().append(true).open("target/metrics/metrics.json")?;
-                writeln!(file, "{}", metrics.json())?;
-            }
-
             let _d = sh.push_dir("metrics");
+
+            let mut file = fs::File::options().append(true).open("metrics.json")?;
+            writeln!(file, "{}", metrics.json())?;
             cmd!(sh, "git add .").run()?;
             cmd!(sh, "git -c user.name=Bot -c user.email=dummy@example.com commit --message 📈")
                 .run()?;


Test patch:
diff --git a/crates/hir_ty/src/tests/method_resolution.rs b/crates/hir_ty/src/tests/method_resolution.rs
index 56b8db1319..9700b388aa 100644
--- a/crates/hir_ty/src/tests/method_resolution.rs
+++ b/crates/hir_ty/src/tests/method_resolution.rs
@@ -925,6 +925,7 @@ fn test() { S2.into(); }
 
 #[test]
 fn method_resolution_overloaded_method() {
+    cov_mark::check!(impl_self_type_match_without_receiver);
     check_types(
         r#"
 struct Wrapper<T>(T);
@@ -953,33 +954,6 @@ fn main() {
     );
 }
 
-#[test]
-fn method_resolution_overloaded_const() {
-    cov_mark::check!(const_candidate_self_type_mismatch);
-    check_types(
-        r#"
-struct Wrapper<T>(T);
-struct Foo<T>(T);
-struct Bar<T>(T);
-
-impl<T> Wrapper<Foo<T>> {
-    pub const VALUE: Foo<T>;
-}
-
-impl<T> Wrapper<Bar<T>> {
-    pub const VALUE: Bar<T>;
-}
-
-fn main() {
-    let a = Wrapper::<Foo<f32>>::VALUE;
-    let b = Wrapper::<Bar<f32>>::VALUE;
-    (a, b);
-  //^^^^^^ (Foo<f32>, Bar<f32>)
-}
-"#,
-    );
-}
-
 #[test]
 fn method_resolution_encountering_fn_type() {
     check_types(
@@ -1283,37 +1257,6 @@ mod b {
     )
 }
 
-#[test]
-fn trait_vs_private_inherent_const() {
-    cov_mark::check!(const_candidate_not_visible);
-    check(
-        r#"
-mod a {
-    pub struct Foo;
-    impl Foo {
-        const VALUE: u32 = 2;
-    }
-    pub trait Trait {
-        const VALUE: usize;
-    }
-    impl Trait for Foo {
-        const VALUE: usize = 3;
-    }
-
-    fn foo() {
-        let x = Foo::VALUE;
-            //  ^^^^^^^^^^ type: u32
-    }
-}
-use a::Trait;
-fn foo() {
-    let x = a::Foo::VALUE;
-         // ^^^^^^^^^^^^^ type: usize
-}
-"#,
-    )
-}
-
 #[test]
 fn trait_impl_in_unnamed_const() {
     check_types(
diff --git a/crates/hir_ty/src/tests/regression.rs b/crates/hir_ty/src/tests/regression.rs
index 7a7470fd35..63faecbfaf 100644
--- a/crates/hir_ty/src/tests/regression.rs
+++ b/crates/hir_ty/src/tests/regression.rs
@@ -1454,26 +1454,3 @@ fn regression_11688_1() {
         "#,
     );
 }
-
-#[test]
-fn regression_11688_2() {
-    check_types(
-        r#"
-        union MaybeUninit<T> {
-            uninit: (),
-            value: T,
-        }
-
-        impl<T> MaybeUninit<T> {
-            fn uninit_array<const LEN: usize>() -> [Self; LEN] {
-                loop {}
-            }
-        }
-
-        fn main() {
-            let x = MaybeUninit::<i32>::uninit_array::<1>();
-              //^ [MaybeUninit<i32>; 1]
-        }
-        "#,
-    );
-}
diff --git a/crates/hir_ty/src/tests/traits.rs b/crates/hir_ty/src/tests/traits.rs
index a82b8cb466..5daffb2c5e 100644
--- a/crates/hir_ty/src/tests/traits.rs
+++ b/crates/hir_ty/src/tests/traits.rs
@@ -3609,40 +3609,3 @@ fn f<F: Foo>() {
         "#]],
     );
 }
-
-#[test]
-fn dyn_map() {
-    check_types(
-        r#"
-pub struct Key<K, V, P = (K, V)> {}
-
-pub trait Policy {
-    type K;
-    type V;
-}
-
-impl<K, V> Policy for (K, V) {
-    type K = K;
-    type V = V;
-}
-
-pub struct KeyMap<KEY> {}
-
-impl<P: Policy> KeyMap<Key<P::K, P::V, P>> {
-    pub fn get(&self, key: &P::K) -> P::V {
-        loop {}
-    }
-}
-
-struct Fn {}
-struct FunctionId {}
-
-fn test() {
-    let key_map: &KeyMap<Key<Fn, FunctionId>> = loop {};
-    let key;
-    let result = key_map.get(key);
-      //^^^^^^ FunctionId
-}
-"#,
-    )
-}
diff --git a/crates/ide_assists/src/tests/generated.rs b/crates/ide_assists/src/tests/generated.rs
index bf69a94c27..485b807d05 100644
--- a/crates/ide_assists/src/tests/generated.rs
+++ b/crates/ide_assists/src/tests/generated.rs
@@ -922,7 +922,6 @@ impl Version {
     /// Returns `true` if the version is [`Minor`].
     ///
     /// [`Minor`]: Version::Minor
-    #[must_use]
     fn is_minor(&self) -> bool {
         matches!(self, Self::Minor)
     }
@@ -1037,7 +1036,6 @@ struct Person {
 
 impl Person {
     /// Get a reference to the person's name.
-    #[must_use]
     fn $0name(&self) -> &str {
         self.name.as_ref()
     }
@@ -1062,7 +1060,6 @@ struct Person {
 
 impl Person {
     /// Get a mutable reference to the person's name.
-    #[must_use]
     fn $0name_mut(&mut self) -> &mut String {
         &mut self.name
     }
@@ -1100,7 +1097,6 @@ fn doctest_generate_is_empty_from_len() {
 struct MyStruct { data: Vec<String> }
 
 impl MyStruct {
-    #[must_use]
     p$0ub fn len(&self) -> usize {
         self.data.len()
     }
@@ -1110,12 +1106,10 @@ impl MyStruct {
 struct MyStruct { data: Vec<String> }
 
 impl MyStruct {
-    #[must_use]
     pub fn len(&self) -> usize {
         self.data.len()
     }
 
-    #[must_use]
     pub fn is_empty(&self) -> bool {
         self.len() == 0
     }
@@ -1139,7 +1133,6 @@ struct Ctx<T: Clone> {
 }
 
 impl<T: Clone> Ctx<T> {
-    #[must_use]
     fn $0new(data: T) -> Self { Self { data } }
 }
 "#####,
diff --git a/crates/ide_completion/src/tests/pattern.rs b/crates/ide_completion/src/tests/pattern.rs
index 405a128b7e..50d5e01979 100644
--- a/crates/ide_completion/src/tests/pattern.rs
+++ b/crates/ide_completion/src/tests/pattern.rs
@@ -306,7 +306,6 @@ fn func() {
             ev TupleV(…)   TupleV(u32)
             ev RecordV {…} RecordV { field: u32 }
             ev UnitV       UnitV
-            ct ASSOC_CONST const ASSOC_CONST: ()
         "#]],
     );
 }
@@ -445,92 +444,3 @@ fn foo() {
         expect![[r#""#]],
     );
 }
-
-#[test]
-fn completes_associated_const() {
-    check_empty(
-        r#"
-#[derive(PartialEq, Eq)]
-struct Ty(u8);
-
-impl Ty {
-    const ABC: Self = Self(0);
-}
-
-fn f(t: Ty) {
-    match t {
-        Ty::$0 => {}
-        _ => {}
-    }
-}
-"#,
-        expect![[r#"
-            ct ABC const ABC: Self
-        "#]],
-    );
-
-    check_empty(
-        r#"
-enum MyEnum {}
-
-impl MyEnum {
-    pub const A: i32 = 123;
-    pub const B: i32 = 456;
-}
-
-fn f(e: MyEnum) {
-    match e {
-        MyEnum::$0 => {}
-        _ => {}
-    }
-}
-"#,
-        expect![[r#"
-            ct A pub const A: i32
-            ct B pub const B: i32
-        "#]],
-    );
-
-    check_empty(
-        r#"
-union U {
-    i: i32,
-    f: f32,
-}
-
-impl U {
-    pub const C: i32 = 123;
-    pub const D: i32 = 456;
-}
-
-fn f(u: U) {
-    match u {
-        U::$0 => {}
-        _ => {}
-    }
-}
-"#,
-        expect![[r#"
-            ct C pub const C: i32
-            ct D pub const D: i32
-        "#]],
-    );
-
-    check_empty(
-        r#"
-#[lang = "u32"]
-impl u32 {
-    pub const MIN: Self = 0;
-}
-
-fn f(v: u32) {
-    match v {
-        u32::$0
-    }
-}
-        "#,
-        expect![[r#"
-            ct MIN pub const MIN: Self
-        "#]],
-    );
-}
diff --git a/crates/rust-analyzer/tests/slow-tests/tidy.rs b/crates/rust-analyzer/tests/slow-tests/tidy.rs
index 1d04caa28a..9e53aa847d 100644
--- a/crates/rust-analyzer/tests/slow-tests/tidy.rs
+++ b/crates/rust-analyzer/tests/slow-tests/tidy.rs
@@ -227,7 +227,6 @@ fn check_licenses() {
 Apache-2.0
 Apache-2.0 OR BSL-1.0
 Apache-2.0 OR MIT
-Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT
 Apache-2.0/MIT
 BSD-3-Clause
 CC0-1.0 OR Artistic-2.0
diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs
index 0fa38bb97c..4438a12093 100644
--- a/crates/test_utils/src/lib.rs
+++ b/crates/test_utils/src/lib.rs
@@ -306,15 +306,15 @@ fn extract_line_annotations(mut line: &str) -> Vec<LineAnnotation> {
         let end_marker = line_no_caret.find(|c| c == '$');
         let next = line_no_caret.find(marker).map_or(line.len(), |it| it + len);
 
-        let mut content = match end_marker {
-            Some(end_marker)
-                if end_marker < next
-                    && line_no_caret[end_marker..]
+        let cond = |end_marker| {
+            end_marker < next
+                && (line_no_caret[end_marker + 1..].is_empty()
+                    || line_no_caret[end_marker + 1..]
                         .strip_prefix(|c: char| c.is_whitespace() || c == '^')
-                        .is_some() =>
-            {
-                &line_no_caret[..end_marker]
-            }
+                        .is_some())
+        };
+        let mut content = match end_marker {
+            Some(end_marker) if cond(end_marker) => &line_no_caret[..end_marker],
             _ => line_no_caret[..next - len].trim_end(),
         };