about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2023-03-15 07:03:57 +0000
committerbors <bors@rust-lang.org>2023-03-15 07:03:57 +0000
commit5fde770aec7945a5d551a0ad3357c0fd85eef10c (patch)
tree88c74d646060429f93b7790a9c7fad32a4c38a78 /src
parent163f354ef617054af121e3bcc72ccec9cdb2f482 (diff)
parentf18c39e98cd5f5c84be6c1671d9a43dca5fe10f5 (diff)
downloadrust-5fde770aec7945a5d551a0ad3357c0fd85eef10c.tar.gz
rust-5fde770aec7945a5d551a0ad3357c0fd85eef10c.zip
Auto merge of #2815 - saethlin:rustup, r=oli-obk
rustup
Diffstat (limited to 'src')
-rw-r--r--src/bootstrap/CHANGELOG.md1
-rw-r--r--src/bootstrap/Cargo.toml5
-rw-r--r--src/bootstrap/README.md2
-rw-r--r--src/bootstrap/bin/main.rs25
-rw-r--r--src/bootstrap/builder.rs1
-rw-r--r--src/bootstrap/config.rs4
-rw-r--r--src/bootstrap/config/tests.rs35
-rwxr-xr-xsrc/bootstrap/configure.py6
-rw-r--r--src/bootstrap/dist.rs2
-rw-r--r--src/bootstrap/doc.rs1
-rw-r--r--src/bootstrap/native.rs4
-rw-r--r--src/ci/docker/host-x86_64/mingw-check/Dockerfile4
-rw-r--r--src/ci/docker/host-x86_64/x86_64-gnu-llvm-14/Dockerfile4
-rw-r--r--src/ci/github-actions/ci.yml1
-rw-r--r--src/ci/github-actions/problem_matchers.json15
-rwxr-xr-xsrc/ci/scripts/collect-cpu-stats.sh3
-rwxr-xr-xsrc/ci/scripts/run-build-from-ci.sh2
-rwxr-xr-xsrc/ci/scripts/upload-artifacts.sh2
-rw-r--r--src/doc/footer.inc2
m---------src/doc/nomicon0
m---------src/doc/reference0
m---------src/doc/rustc-dev-guide0
-rw-r--r--src/doc/rustc/src/instrument-coverage.md2
-rw-r--r--src/doc/unstable-book/src/language-features/box-patterns.md2
-rw-r--r--src/doc/unstable-book/src/language-features/box-syntax.md22
-rw-r--r--src/doc/unstable-book/src/the-unstable-book.md23
-rw-r--r--src/librustdoc/formats/cache.rs1
-rw-r--r--src/librustdoc/html/format.rs136
-rw-r--r--src/librustdoc/html/render/context.rs29
-rw-r--r--src/librustdoc/html/render/mod.rs733
-rw-r--r--src/librustdoc/html/render/print_item.rs9
-rw-r--r--src/librustdoc/html/render/search_index.rs23
-rw-r--r--src/librustdoc/html/render/sidebar.rs561
-rw-r--r--src/librustdoc/html/static/COPYRIGHT.txt4
-rw-r--r--src/librustdoc/html/static/fonts/FiraSans-LICENSE.txt4
-rw-r--r--src/librustdoc/html/static/fonts/NanumBarunGothic-LICENSE.txt4
-rw-r--r--src/librustdoc/html/static/fonts/SourceCodePro-LICENSE.txt4
-rw-r--r--src/librustdoc/html/static/fonts/SourceSerif4-LICENSE.md5
-rw-r--r--src/librustdoc/html/static/js/search.js397
-rw-r--r--src/librustdoc/html/templates/item_info.html7
-rw-r--r--src/librustdoc/html/templates/short_item_info.html23
-rw-r--r--src/librustdoc/html/templates/sidebar.html37
-rw-r--r--src/librustdoc/passes/collect_intra_doc_links.rs4
-rw-r--r--src/librustdoc/visit_ast.rs9
-rw-r--r--src/tools/clippy/CHANGELOG.md153
-rw-r--r--src/tools/clippy/COPYRIGHT4
-rw-r--r--src/tools/clippy/Cargo.toml2
-rw-r--r--src/tools/clippy/README.md4
-rw-r--r--src/tools/clippy/book/src/development/proposals/syntax-tree-patterns.md14
-rw-r--r--src/tools/clippy/clippy_lints/Cargo.toml2
-rw-r--r--src/tools/clippy/clippy_lints/src/almost_complete_range.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/cast_slice_from_raw_parts.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/collection_is_never_read.rs122
-rw-r--r--src/tools/clippy/clippy_lints/src/declared_lints.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/default_instead_of_iter_empty.rs16
-rw-r--r--src/tools/clippy/clippy_lints/src/dereference.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/derivable_impls.rs21
-rw-r--r--src/tools/clippy/clippy_lints/src/exit.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/fn_null_check.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/format.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/functions/misnamed_getters.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/functions/mod.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/implicit_saturating_add.rs16
-rw-r--r--src/tools/clippy/clippy_lints/src/instant_subtraction.rs13
-rw-r--r--src/tools/clippy/clippy_lints/src/len_zero.rs125
-rw-r--r--src/tools/clippy/clippy_lints/src/let_underscore.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs45
-rw-r--r--src/tools/clippy/clippy_lints/src/lib.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_async_fn.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_bits.rs11
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs15
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/match_result_ok.rs25
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/mod.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/mod.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_assert_message.rs82
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_doc.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs37
-rw-r--r--src/tools/clippy/clippy_lints/src/neg_multiply.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs13
-rw-r--r--src/tools/clippy/clippy_lints/src/permissions_set_readonly_false.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/redundant_async_block.rs84
-rw-r--r--src/tools/clippy/clippy_lints/src/size_of_ref.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/swap.rs111
-rw-r--r--src/tools/clippy/clippy_lints/src/transmute/mod.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/transmute/utils.rs57
-rw-r--r--src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/use_self.rs15
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/author.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs23
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/mod.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/write.rs148
-rw-r--r--src/tools/clippy/clippy_utils/Cargo.toml2
-rw-r--r--src/tools/clippy/clippy_utils/src/ast_utils.rs2
-rw-r--r--src/tools/clippy/clippy_utils/src/lib.rs13
-rw-r--r--src/tools/clippy/clippy_utils/src/macros.rs117
-rw-r--r--src/tools/clippy/clippy_utils/src/sugg.rs1
-rw-r--r--src/tools/clippy/clippy_utils/src/ty.rs6
-rw-r--r--src/tools/clippy/declare_clippy_lint/Cargo.toml2
-rw-r--r--src/tools/clippy/lintcheck/Cargo.toml14
-rw-r--r--src/tools/clippy/lintcheck/README.md9
-rw-r--r--src/tools/clippy/lintcheck/src/config.rs160
-rw-r--r--src/tools/clippy/lintcheck/src/popular-crates.rs65
-rw-r--r--src/tools/clippy/rust-toolchain2
-rw-r--r--src/tools/clippy/rustc_tools_util/README.md4
-rw-r--r--src/tools/clippy/src/driver.rs2
-rw-r--r--src/tools/clippy/src/main.rs2
-rw-r--r--src/tools/clippy/tests/dogfood.rs19
-rw-r--r--src/tools/clippy/tests/ui/arithmetic_side_effects.rs28
-rw-r--r--src/tools/clippy/tests/ui/arithmetic_side_effects.stderr288
-rw-r--r--src/tools/clippy/tests/ui/async_yields_async.fixed1
-rw-r--r--src/tools/clippy/tests/ui/async_yields_async.rs1
-rw-r--r--src/tools/clippy/tests/ui/async_yields_async.stderr12
-rw-r--r--src/tools/clippy/tests/ui/author/blocks.stdout6
-rw-r--r--src/tools/clippy/tests/ui/boxed_local.rs35
-rw-r--r--src/tools/clippy/tests/ui/boxed_local.stderr8
-rw-r--r--src/tools/clippy/tests/ui/collection_is_never_read.rs165
-rw-r--r--src/tools/clippy/tests/ui/collection_is_never_read.stderr52
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-10148.rs9
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-10148.stderr12
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-6179.rs2
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-rust-107877.rs17
-rw-r--r--src/tools/clippy/tests/ui/default_numeric_fallback_f64.fixed3
-rw-r--r--src/tools/clippy/tests/ui/default_numeric_fallback_f64.rs3
-rw-r--r--src/tools/clippy/tests/ui/default_numeric_fallback_f64.stderr48
-rw-r--r--src/tools/clippy/tests/ui/default_numeric_fallback_i32.fixed3
-rw-r--r--src/tools/clippy/tests/ui/default_numeric_fallback_i32.rs3
-rw-r--r--src/tools/clippy/tests/ui/default_numeric_fallback_i32.stderr52
-rw-r--r--src/tools/clippy/tests/ui/derivable_impls.fixed37
-rw-r--r--src/tools/clippy/tests/ui/derivable_impls.rs37
-rw-r--r--src/tools/clippy/tests/ui/erasing_op.rs8
-rw-r--r--src/tools/clippy/tests/ui/erasing_op.stderr10
-rw-r--r--src/tools/clippy/tests/ui/format.fixed6
-rw-r--r--src/tools/clippy/tests/ui/format.rs6
-rw-r--r--src/tools/clippy/tests/ui/format.stderr30
-rw-r--r--src/tools/clippy/tests/ui/impl_trait_in_params.stderr4
-rw-r--r--src/tools/clippy/tests/ui/implicit_clone.fixed2
-rw-r--r--src/tools/clippy/tests/ui/implicit_clone.rs2
-rw-r--r--src/tools/clippy/tests/ui/integer_arithmetic.rs2
-rw-r--r--src/tools/clippy/tests/ui/len_without_is_empty.rs92
-rw-r--r--src/tools/clippy/tests/ui/len_without_is_empty.stderr20
-rw-r--r--src/tools/clippy/tests/ui/let_unit.fixed4
-rw-r--r--src/tools/clippy/tests/ui/let_unit.rs4
-rw-r--r--src/tools/clippy/tests/ui/let_with_type_underscore.rs19
-rw-r--r--src/tools/clippy/tests/ui/let_with_type_underscore.stderr39
-rw-r--r--src/tools/clippy/tests/ui/manual_rem_euclid.fixed1
-rw-r--r--src/tools/clippy/tests/ui/manual_rem_euclid.rs1
-rw-r--r--src/tools/clippy/tests/ui/manual_rem_euclid.stderr20
-rw-r--r--src/tools/clippy/tests/ui/match_result_ok.fixed2
-rw-r--r--src/tools/clippy/tests/ui/match_result_ok.stderr2
-rw-r--r--src/tools/clippy/tests/ui/missing_assert_message.rs84
-rw-r--r--src/tools/clippy/tests/ui/missing_assert_message.stderr131
-rw-r--r--src/tools/clippy/tests/ui/missing_doc.stderr70
-rw-r--r--src/tools/clippy/tests/ui/missing_doc_impl.stderr56
-rw-r--r--src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.rs28
-rw-r--r--src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.stderr62
-rw-r--r--src/tools/clippy/tests/ui/new_ret_no_self.rs2
-rw-r--r--src/tools/clippy/tests/ui/no_effect.rs3
-rw-r--r--src/tools/clippy/tests/ui/no_effect.stderr38
-rw-r--r--src/tools/clippy/tests/ui/overflow_check_conditional.rs9
-rw-r--r--src/tools/clippy/tests/ui/overflow_check_conditional.stderr16
-rw-r--r--src/tools/clippy/tests/ui/redundant_async_block.fixed64
-rw-r--r--src/tools/clippy/tests/ui/redundant_async_block.rs64
-rw-r--r--src/tools/clippy/tests/ui/redundant_async_block.stderr28
-rw-r--r--src/tools/clippy/tests/ui/redundant_closure_call_fixable.fixed1
-rw-r--r--src/tools/clippy/tests/ui/redundant_closure_call_fixable.rs1
-rw-r--r--src/tools/clippy/tests/ui/redundant_closure_call_fixable.stderr12
-rw-r--r--src/tools/clippy/tests/ui/swap.fixed6
-rw-r--r--src/tools/clippy/tests/ui/swap.stderr24
-rw-r--r--src/tools/clippy/tests/ui/trailing_empty_array.rs2
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_operation.fixed2
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_operation.rs2
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_operation.stderr46
-rw-r--r--src/tools/clippy/tests/ui/use_self.fixed10
-rw-r--r--src/tools/clippy/tests/ui/use_self.rs10
-rw-r--r--src/tools/collect-license-metadata/src/licenses.rs9
-rw-r--r--src/tools/collect-license-metadata/src/path_tree.rs96
-rw-r--r--src/tools/generate-copyright/src/main.rs6
-rw-r--r--src/tools/miri/rust-version2
-rw-r--r--src/tools/miri/tests/fail/function_pointers/execute_memory.rs4
-rw-r--r--src/tools/miri/tests/pass/drop_empty_slice.rs4
-rw-r--r--src/tools/miri/tests/pass/dst-struct.rs6
-rw-r--r--src/tools/miri/tests/pass/heap.rs7
-rw-r--r--src/tools/miri/tests/pass/intrinsics-integer.rs11
-rw-r--r--src/tools/miri/tests/pass/intrinsics-math.rs11
-rw-r--r--src/tools/miri/tests/pass/issues/issue-30530.rs11
-rw-r--r--src/tools/miri/tests/pass/issues/issue-3794.rs4
-rw-r--r--src/tools/miri/tests/pass/move-arg-2-unique.rs4
-rw-r--r--src/tools/miri/tests/pass/move-arg-3-unique.rs4
-rw-r--r--src/tools/miri/tests/pass/mpsc.rs14
-rw-r--r--src/tools/miri/tests/pass/regions-lifetime-nonfree-late-bound.rs6
-rw-r--r--src/tools/miri/tests/pass/tag-align-dyn-u64.rs11
-rw-r--r--src/tools/rust-analyzer/Cargo.lock2
-rw-r--r--src/tools/rust-analyzer/crates/flycheck/src/lib.rs109
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/attr.rs48
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/body.rs31
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs200
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs93
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/body/scope.rs50
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/body/tests/block.rs22
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/child_by_source.rs3
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/data.rs37
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/db.rs11
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/expr.rs69
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/generics.rs44
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/import_map.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs1
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs17
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs137
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs29
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/keys.rs5
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs6
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/lib.rs17
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs1
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres.rs18
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs29
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs1
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/path.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/path/lower.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/resolver.rs258
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/type_ref.rs62
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/visibility.rs7
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/lib.rs26
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/Cargo.toml1
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/builder.rs9
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs3
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs7
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs503
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs955
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/db.rs25
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs5
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs31
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs3
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs6
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/display.rs187
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer.rs215
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs60
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs776
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs345
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs147
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs5
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs19
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/interner.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/layout.rs16
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs13
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs21
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/lib.rs49
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/lower.rs30
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs121
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir.rs863
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs223
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs1253
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs1577
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs237
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs348
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests.rs38
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs3
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/diagnostics.rs21
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs30
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs20
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs24
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/utils.rs7
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/attrs.rs5
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/db.rs5
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/diagnostics.rs47
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/display.rs49
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/from_id.rs13
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/has_source.rs21
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/lib.rs512
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/semantics.rs36
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs38
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs42
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/symbols.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_explicit_type.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs19
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs85
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs25
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs148
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_is_empty_from_len.rs9
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_local_variable.rs11
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs134
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_if_let_with_match.rs27
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs310
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_or_with_or_else.rs364
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/lib.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs84
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs5
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/context.rs15
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/item.rs3
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/render.rs3
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs59
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/active_parameter.rs1
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs64
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/defs.rs21
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/helpers.rs10
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/lib.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/rename.rs105
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/search.rs50
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/source_change.rs8
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/use_trivial_constructor.rs (renamed from src/tools/rust-analyzer/crates/ide-db/src/use_trivial_contructor.rs)0
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs23
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/expected_function.rs39
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs5
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs625
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_assoc_item.rs38
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs13
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs86
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs148
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs131
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs10
-rw-r--r--src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/doc_links.rs15
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/file_structure.rs79
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/goto_definition.rs25
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/highlight_related.rs57
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/hover.rs1
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/hover/render.rs31
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/hover/tests.rs350
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs5
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs117
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs12
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs10
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/lib.rs5
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/markup.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/moniker.rs3
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/move_item.rs1
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/navigation_target.rs26
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/references.rs32
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/rename.rs22
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/runnables.rs290
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/signature_help.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/static_index.rs1
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs5
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs1
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs1
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html2
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/view_mir.rs29
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/grammar.rs4
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/grammar/attributes.rs2
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs28
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs10
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/grammar/items/traits.rs2
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/grammar/paths.rs6
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs1
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/err/0049_let_else_right_curly_brace_for.rast58
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/err/0049_let_else_right_curly_brace_for.rs6
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/err/0050_let_else_right_curly_brace_loop.rast46
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/err/0050_let_else_right_curly_brace_loop.rs6
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/err/0051_let_else_right_curly_brace_match.rast85
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/err/0051_let_else_right_curly_brace_match.rs8
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/err/0052_let_else_right_curly_brace_while.rast49
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/err/0052_let_else_right_curly_brace_while.rs6
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/err/0053_let_else_right_curly_brace_if.rast57
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/err/0053_let_else_right_curly_brace_if.rs7
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0016_angled_path_without_qual.rast49
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0016_angled_path_without_qual.rs2
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0017_let_else_right_curly_brace.rast69
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0017_let_else_right_curly_brace.rs1
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0151_trait_alias.rast2
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0177_trait_alias_where_clause.rast4
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/build_scripts.rs84
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs2
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/sysroot.rs21
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/tests.rs9
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/workspace.rs60
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/default_12483297303756020505_0.profrawbin25152 -> 0 bytes
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs187
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs4
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs5
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs5
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cli/ssr.rs5
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs51
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/handlers.rs10
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_ext.rs8
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs69
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs99
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/to_proto.rs4
-rw-r--r--src/tools/rust-analyzer/crates/syntax/Cargo.toml1
-rw-r--r--src/tools/rust-analyzer/crates/syntax/rust.ungram10
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast.rs12
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs43
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs75
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/traits.rs4
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/tests/ast_src.rs1
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/tests/sourcegen_ast.rs1
-rw-r--r--src/tools/rust-analyzer/crates/test-utils/src/fixture.rs4
-rw-r--r--src/tools/rust-analyzer/crates/test-utils/src/minicore.rs49
-rw-r--r--src/tools/rust-analyzer/crates/toolchain/src/lib.rs21
-rw-r--r--src/tools/rust-analyzer/docs/dev/lsp-extensions.md13
-rw-r--r--src/tools/rust-analyzer/docs/user/generated_config.adoc15
-rw-r--r--src/tools/rust-analyzer/editors/code/package.json31
-rw-r--r--src/tools/rust-analyzer/editors/code/src/commands.ts35
-rw-r--r--src/tools/rust-analyzer/editors/code/src/ctx.ts34
-rw-r--r--src/tools/rust-analyzer/editors/code/src/lsp_ext.ts3
-rw-r--r--src/tools/rust-analyzer/editors/code/src/main.ts2
-rw-r--r--src/tools/rust-analyzer/editors/code/src/toolchain.ts30
-rw-r--r--src/tools/rust-analyzer/lib/la-arena/src/map.rs6
-rwxr-xr-xsrc/tools/rust-installer/combine-installers.sh9
-rwxr-xr-xsrc/tools/rust-installer/gen-install-script.sh9
-rwxr-xr-xsrc/tools/rust-installer/gen-installer.sh9
-rw-r--r--src/tools/rust-installer/install-template.sh9
-rwxr-xr-xsrc/tools/rust-installer/make-tarballs.sh9
-rw-r--r--src/tools/rustdoc-gui/.eslintrc.js96
-rw-r--r--src/tools/rustdoc-gui/tester.js16
-rw-r--r--src/tools/rustdoc-js/.eslintrc.js96
-rw-r--r--src/tools/rustdoc-js/tester.js90
-rw-r--r--src/tools/rustfmt/src/closures.rs2
-rw-r--r--src/tools/rustfmt/src/expr.rs5
-rw-r--r--src/tools/rustfmt/src/matches.rs1
-rw-r--r--src/tools/rustfmt/src/utils.rs1
-rw-r--r--src/tools/rustfmt/tests/source/expr.rs11
-rw-r--r--src/tools/rustfmt/tests/target/configs/combine_control_expr/false.rs6
-rw-r--r--src/tools/rustfmt/tests/target/configs/combine_control_expr/true.rs6
-rw-r--r--src/tools/rustfmt/tests/target/expr.rs14
-rw-r--r--src/tools/tidy/src/pal.rs2
-rw-r--r--src/tools/tidy/src/style.rs69
434 files changed, 16862 insertions, 5231 deletions
diff --git a/src/bootstrap/CHANGELOG.md b/src/bootstrap/CHANGELOG.md
index 648eb553c78..54d0d8a8ec2 100644
--- a/src/bootstrap/CHANGELOG.md
+++ b/src/bootstrap/CHANGELOG.md
@@ -47,6 +47,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 - Add `--keep-stage-std`, which behaves like `keep-stage` but allows the stage
   0 compiler artifacts (i.e., stage1/bin/rustc) to be rebuilt if changed
   [#77120](https://github.com/rust-lang/rust/pull/77120).
+- File locking is now used to avoid collisions between multiple running instances of `x.py` (e.g. when using `rust-analyzer` and `x.py` at the same time). Note that Solaris and possibly other non Unix and non Windows systems don't support it [#108607](https://github.com/rust-lang/rust/pull/108607). This might possibly lead to build data corruption.
 
 
 ## [Version 1] - 2020-09-11
diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml
index 663987f113c..afe3fc1741b 100644
--- a/src/bootstrap/Cargo.toml
+++ b/src/bootstrap/Cargo.toml
@@ -32,7 +32,6 @@ test = false
 [dependencies]
 build_helper = { path = "../tools/build_helper" }
 cmake = "0.1.38"
-fd-lock = "3.0.8"
 filetime = "0.2"
 getopts = "0.2.19"
 cc = "1.0.69"
@@ -56,6 +55,10 @@ walkdir = "2"
 # Dependencies needed by the build-metrics feature
 sysinfo = { version = "0.26.0", optional = true }
 
+# Solaris doesn't support flock() and thus fd-lock is not option now
+[target.'cfg(not(target_os = "solaris"))'.dependencies]
+fd-lock = "3.0.8"
+
 [target.'cfg(windows)'.dependencies.winapi]
 version = "0.3"
 features = [
diff --git a/src/bootstrap/README.md b/src/bootstrap/README.md
index 71eee8968e2..253d504d7bd 100644
--- a/src/bootstrap/README.md
+++ b/src/bootstrap/README.md
@@ -185,7 +185,7 @@ Some general areas that you may be interested in modifying are:
 If you make a major change, please remember to:
 
 + Update `VERSION` in `src/bootstrap/main.rs`.
-* Update `changelog-seen = N` in `config.toml.example`.
+* Update `changelog-seen = N` in `config.example.toml`.
 * Add an entry in `src/bootstrap/CHANGELOG.md`.
 
 A 'major change' includes
diff --git a/src/bootstrap/bin/main.rs b/src/bootstrap/bin/main.rs
index 3856bb64fb3..912d875e445 100644
--- a/src/bootstrap/bin/main.rs
+++ b/src/bootstrap/bin/main.rs
@@ -7,15 +7,18 @@
 
 use std::env;
 
-use bootstrap::{t, Build, Config, Subcommand, VERSION};
+#[cfg(all(any(unix, windows), not(target_os = "solaris")))]
+use bootstrap::t;
+use bootstrap::{Build, Config, Subcommand, VERSION};
 
 fn main() {
     let args = env::args().skip(1).collect::<Vec<_>>();
     let config = Config::parse(&args);
 
-    let mut build_lock;
-    let _build_lock_guard;
-    if cfg!(any(unix, windows)) {
+    #[cfg(all(any(unix, windows), not(target_os = "solaris")))]
+    {
+        let mut build_lock;
+        let _build_lock_guard;
         let path = config.out.join("lock");
         build_lock = fd_lock::RwLock::new(t!(std::fs::File::create(&path)));
         _build_lock_guard = match build_lock.try_write() {
@@ -30,9 +33,9 @@ fn main() {
                 t!(build_lock.write())
             }
         };
-    } else {
-        println!("warning: file locking not supported for target, not locking build directory");
     }
+    #[cfg(any(not(any(unix, windows)), target_os = "solaris"))]
+    println!("warning: file locking not supported for target, not locking build directory");
 
     // check_version warnings are not printed during setup
     let changelog_suggestion =
@@ -44,8 +47,8 @@ fn main() {
     if suggest_setup {
         println!("warning: you have not made a `config.toml`");
         println!(
-            "help: consider running `./x.py setup` or copying `config.toml.example` by running \
-            `cp config.toml.example config.toml`"
+            "help: consider running `./x.py setup` or copying `config.example.toml` by running \
+            `cp config.example.toml config.toml`"
         );
     } else if let Some(suggestion) = &changelog_suggestion {
         println!("{}", suggestion);
@@ -57,8 +60,8 @@ fn main() {
     if suggest_setup {
         println!("warning: you have not made a `config.toml`");
         println!(
-            "help: consider running `./x.py setup` or copying `config.toml.example` by running \
-            `cp config.toml.example config.toml`"
+            "help: consider running `./x.py setup` or copying `config.example.toml` by running \
+            `cp config.example.toml config.toml`"
         );
     } else if let Some(suggestion) = &changelog_suggestion {
         println!("{}", suggestion);
@@ -125,7 +128,7 @@ fn get_lock_owner(f: &std::path::Path) -> Option<u64> {
     })
 }
 
-#[cfg(not(target_os = "linux"))]
+#[cfg(not(any(target_os = "linux", target_os = "solaris")))]
 fn get_lock_owner(_: &std::path::Path) -> Option<u64> {
     // FIXME: Implement on other OS's
     None
diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs
index 47d742ef796..bb07ca1908e 100644
--- a/src/bootstrap/builder.rs
+++ b/src/bootstrap/builder.rs
@@ -741,6 +741,7 @@ impl<'a> Builder<'a> {
                 doc::EmbeddedBook,
                 doc::EditionGuide,
                 doc::StyleGuide,
+                doc::Tidy,
             ),
             Kind::Dist => describe!(
                 dist::Docs,
diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs
index 4f417d36511..fc5aa8a245d 100644
--- a/src/bootstrap/config.rs
+++ b/src/bootstrap/config.rs
@@ -55,7 +55,7 @@ pub enum DryRun {
 /// Note that this structure is not decoded directly into, but rather it is
 /// filled out from the decoded forms of the structs below. For documentation
 /// each field, see the corresponding fields in
-/// `config.toml.example`.
+/// `config.example.toml`.
 #[derive(Default)]
 #[cfg_attr(test, derive(Clone))]
 pub struct Config {
@@ -325,7 +325,7 @@ impl std::str::FromStr for SplitDebuginfo {
 
 impl SplitDebuginfo {
     /// Returns the default `-Csplit-debuginfo` value for the current target. See the comment for
-    /// `rust.split-debuginfo` in `config.toml.example`.
+    /// `rust.split-debuginfo` in `config.example.toml`.
     fn default_for_platform(target: &str) -> Self {
         if target.contains("apple") {
             SplitDebuginfo::Unpacked
diff --git a/src/bootstrap/config/tests.rs b/src/bootstrap/config/tests.rs
index 5a105007f21..16dc8c63abc 100644
--- a/src/bootstrap/config/tests.rs
+++ b/src/bootstrap/config/tests.rs
@@ -1,5 +1,5 @@
 use super::{Config, TomlConfig};
-use std::path::Path;
+use std::{env, path::Path};
 
 fn toml(config: &str) -> impl '_ + Fn(&Path) -> TomlConfig {
     |&_| toml::from_str(config).unwrap()
@@ -33,4 +33,35 @@ fn download_ci_llvm() {
     ));
 }
 
-// FIXME: add test for detecting `src` and `out`
+#[test]
+fn detect_src_and_out() {
+    let cfg = parse("");
+
+    // This will bring absolute form of `src/bootstrap` path
+    let current_dir = std::env::current_dir().unwrap();
+
+    // get `src` by moving into project root path
+    let expected_src = current_dir.ancestors().nth(2).unwrap();
+
+    assert_eq!(&cfg.src, expected_src);
+
+    // This should bring output path of bootstrap in absolute form
+    let cargo_target_dir = env::var_os("CARGO_TARGET_DIR")
+        .expect("CARGO_TARGET_DIR must been provided for the test environment from bootstrap");
+
+    // Move to `build` from `build/bootstrap`
+    let expected_out = Path::new(&cargo_target_dir).parent().unwrap();
+    assert_eq!(&cfg.out, expected_out);
+
+    let args: Vec<String> = env::args().collect();
+
+    // Another test for `out` as a sanity check
+    //
+    // This will bring something similar to:
+    //     `{config_toml_place}/build/bootstrap/debug/deps/bootstrap-c7ee91d5661e2804`
+    // `{config_toml_place}` can be anywhere, not just in the rust project directory.
+    let dep = Path::new(args.first().unwrap());
+    let expected_out = dep.ancestors().nth(4).unwrap();
+
+    assert_eq!(&cfg.out, expected_out);
+}
diff --git a/src/bootstrap/configure.py b/src/bootstrap/configure.py
index 5278f0c10b3..b326ae402aa 100755
--- a/src/bootstrap/configure.py
+++ b/src/bootstrap/configure.py
@@ -194,7 +194,7 @@ if '--help' in sys.argv or '-h' in sys.argv:
     print('')
     print('This configure script is a thin configuration shim over the true')
     print('configuration system, `config.toml`. You can explore the comments')
-    print('in `config.toml.example` next to this configure script to see')
+    print('in `config.example.toml` next to this configure script to see')
     print('more information about what each option is. Additionally you can')
     print('pass `--set` as an argument to set arbitrary key/value pairs')
     print('in the TOML configuration if desired')
@@ -367,7 +367,7 @@ for key in known_args:
 
 set('build.configure-args', sys.argv[1:])
 
-# "Parse" the `config.toml.example` file into the various sections, and we'll
+# "Parse" the `config.example.toml` file into the various sections, and we'll
 # use this as a template of a `config.toml` to write out which preserves
 # all the various comments and whatnot.
 #
@@ -380,7 +380,7 @@ section_order = [None]
 targets = {}
 top_level_keys = []
 
-for line in open(rust_dir + '/config.toml.example').read().split("\n"):
+for line in open(rust_dir + '/config.example.toml').read().split("\n"):
     if cur_section == None:
         if line.count('=') == 1:
             top_level_key = line.split('=')[0]
diff --git a/src/bootstrap/dist.rs b/src/bootstrap/dist.rs
index c9384004100..a3d9cb3e10c 100644
--- a/src/bootstrap/dist.rs
+++ b/src/bootstrap/dist.rs
@@ -967,7 +967,7 @@ impl Step for PlainSourceTarball {
             "RELEASES.md",
             "configure",
             "x.py",
-            "config.toml.example",
+            "config.example.toml",
             "Cargo.toml",
             "Cargo.lock",
         ];
diff --git a/src/bootstrap/doc.rs b/src/bootstrap/doc.rs
index cc80763ef44..36fdd4abf4f 100644
--- a/src/bootstrap/doc.rs
+++ b/src/bootstrap/doc.rs
@@ -882,6 +882,7 @@ tool_doc!(
         // "cargo-credential-wincred",
     ]
 );
+tool_doc!(Tidy, "tidy", "src/tools/tidy", ["tidy"]);
 
 #[derive(Ord, PartialOrd, Debug, Copy, Clone, Hash, PartialEq, Eq)]
 pub struct ErrorIndex {
diff --git a/src/bootstrap/native.rs b/src/bootstrap/native.rs
index 909e7d83a15..040e36ea5f8 100644
--- a/src/bootstrap/native.rs
+++ b/src/bootstrap/native.rs
@@ -216,7 +216,7 @@ pub(crate) fn is_ci_llvm_available(config: &Config, asserts: bool) -> bool {
 
 /// Returns true if we're running in CI with modified LLVM (and thus can't download it)
 pub(crate) fn is_ci_llvm_modified(config: &Config) -> bool {
-    CiEnv::is_ci() && {
+    CiEnv::is_ci() && config.rust_info.is_managed_git_subrepository() && {
         // We assume we have access to git, so it's okay to unconditionally pass
         // `true` here.
         let llvm_sha = detect_llvm_sha(config, true);
@@ -286,7 +286,7 @@ impl Step for Llvm {
             (true, true) => "RelWithDebInfo",
         };
 
-        // NOTE: remember to also update `config.toml.example` when changing the
+        // NOTE: remember to also update `config.example.toml` when changing the
         // defaults!
         let llvm_targets = match &builder.config.llvm_targets {
             Some(s) => s,
diff --git a/src/ci/docker/host-x86_64/mingw-check/Dockerfile b/src/ci/docker/host-x86_64/mingw-check/Dockerfile
index 98bd90210d6..515890aef8d 100644
--- a/src/ci/docker/host-x86_64/mingw-check/Dockerfile
+++ b/src/ci/docker/host-x86_64/mingw-check/Dockerfile
@@ -52,4 +52,6 @@ ENV SCRIPT python3 ../x.py --stage 2 test src/tools/expand-yaml-anchors && \
            reuse lint && \
            # Runs checks to ensure that there are no ES5 issues in our JS code.
            es-check es6 ../src/librustdoc/html/static/js/*.js && \
-           eslint -c ../src/librustdoc/html/static/.eslintrc.js ../src/librustdoc/html/static/js/*.js
+           eslint -c ../src/librustdoc/html/static/.eslintrc.js ../src/librustdoc/html/static/js/*.js && \
+           eslint -c ../src/tools/rustdoc-js/.eslintrc.js ../src/tools/rustdoc-js/tester.js && \
+           eslint -c ../src/tools/rustdoc-gui/.eslintrc.js ../src/tools/rustdoc-gui/tester.js
diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-14/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-14/Dockerfile
index db6032f8752..a007bf183ee 100644
--- a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-14/Dockerfile
+++ b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-14/Dockerfile
@@ -62,6 +62,4 @@ ENV SCRIPT ../x.py --stage 2 test --exclude src/tools/tidy && \
            # work.
            #
            ../x.ps1 --stage 2 test tests/ui --pass=check \
-                             --host='' --target=i686-unknown-linux-gnu && \
-           # Run tidy at the very end, after all the other tests.
-           python2.7 ../x.py --stage 2 test src/tools/tidy
+                             --host='' --target=i686-unknown-linux-gnu
diff --git a/src/ci/github-actions/ci.yml b/src/ci/github-actions/ci.yml
index 11f1532bef5..b490b766663 100644
--- a/src/ci/github-actions/ci.yml
+++ b/src/ci/github-actions/ci.yml
@@ -673,7 +673,6 @@ jobs:
                 --target=x86_64-pc-windows-msvc
                 --enable-full-tools
                 --enable-profiler
-                --set rust.lto=thin
               SCRIPT: PGO_HOST=x86_64-pc-windows-msvc python src/ci/stage-build.py python x.py dist bootstrap --include-default-paths
               DIST_REQUIRE_ALL_TOOLS: 1
             <<: *job-windows-xl
diff --git a/src/ci/github-actions/problem_matchers.json b/src/ci/github-actions/problem_matchers.json
new file mode 100644
index 00000000000..37561924b7d
--- /dev/null
+++ b/src/ci/github-actions/problem_matchers.json
@@ -0,0 +1,15 @@
+{
+    "problemMatcher": [
+        {
+            "owner": "tidy-error-file-line",
+            "pattern": [
+                {
+                    "regexp": "^tidy error: /checkout/(.+):(\\d+): (.+)$",
+                    "file": 1,
+                    "line": 2,
+                    "message": 3
+                }
+            ]
+        }
+    ]
+}
diff --git a/src/ci/scripts/collect-cpu-stats.sh b/src/ci/scripts/collect-cpu-stats.sh
index 853b4628fab..44875b54ddc 100755
--- a/src/ci/scripts/collect-cpu-stats.sh
+++ b/src/ci/scripts/collect-cpu-stats.sh
@@ -6,4 +6,5 @@
 set -euo pipefail
 IFS=$'\n\t'
 
-python3 src/ci/cpu-usage-over-time.py &> cpu-usage.csv &
+mkdir -p build
+python3 src/ci/cpu-usage-over-time.py &> build/cpu-usage.csv &
diff --git a/src/ci/scripts/run-build-from-ci.sh b/src/ci/scripts/run-build-from-ci.sh
index c02117f459d..55e75800d91 100755
--- a/src/ci/scripts/run-build-from-ci.sh
+++ b/src/ci/scripts/run-build-from-ci.sh
@@ -10,6 +10,8 @@ source "$(cd "$(dirname "$0")" && pwd)/../shared.sh"
 export CI="true"
 export SRC=.
 
+echo "::add-matcher::src/ci/github-actions/problem_matchers.json"
+
 # Remove any preexisting rustup installation since it can interfere
 # with the cargotest step and its auto-detection of things like Clippy in
 # the environment
diff --git a/src/ci/scripts/upload-artifacts.sh b/src/ci/scripts/upload-artifacts.sh
index ffa1859fc22..9755edb6dce 100755
--- a/src/ci/scripts/upload-artifacts.sh
+++ b/src/ci/scripts/upload-artifacts.sh
@@ -23,7 +23,7 @@ if [[ "${DEPLOY-0}" -eq "1" ]] || [[ "${DEPLOY_ALT-0}" -eq "1" ]]; then
 fi
 
 # CPU usage statistics.
-cp cpu-usage.csv "${upload_dir}/cpu-${CI_JOB_NAME}.csv"
+cp build/cpu-usage.csv "${upload_dir}/cpu-${CI_JOB_NAME}.csv"
 
 # Build metrics generated by x.py.
 cp "${build_dir}/metrics.json" "${upload_dir}/metrics-${CI_JOB_NAME}.json"
diff --git a/src/doc/footer.inc b/src/doc/footer.inc
index 77e151235e8..504fe51156d 100644
--- a/src/doc/footer.inc
+++ b/src/doc/footer.inc
@@ -1,3 +1,4 @@
+<!-- REUSE-IgnoreStart -->
 <footer><p>
 Copyright &copy; 2011 The Rust Project Developers. Licensed under the
 <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache License, Version 2.0</a>
@@ -5,3 +6,4 @@ or the <a href="https://opensource.org/licenses/MIT">MIT license</a>, at your op
 </p><p>
 This file may not be copied, modified, or distributed except according to those terms.
 </p></footer>
+<!-- REUSE-IgnoreEnd -->
diff --git a/src/doc/nomicon b/src/doc/nomicon
-Subproject 79b53665a7c61d171fb8c5ad0b73b371f9ee6ba
+Subproject 1f3e4cd4fd88b5b5d45feb86a11b6d2f93e5a97
diff --git a/src/doc/reference b/src/doc/reference
-Subproject a9afb04b47a84a6753e4dc657348c324c876102
+Subproject 24c87f6663aed55b05d2cc286878f28f2191882
diff --git a/src/doc/rustc-dev-guide b/src/doc/rustc-dev-guide
-Subproject b06dab84083390e0ee1e998f466545a8a1a76a9
+Subproject b1b6d693cd1461e53de4132c1b183ace31cd36e
diff --git a/src/doc/rustc/src/instrument-coverage.md b/src/doc/rustc/src/instrument-coverage.md
index da91e25595c..b0b2f419642 100644
--- a/src/doc/rustc/src/instrument-coverage.md
+++ b/src/doc/rustc/src/instrument-coverage.md
@@ -31,7 +31,7 @@ Rust's source-based code coverage requires the Rust "profiler runtime". Without
 
 The Rust `nightly` distribution channel includes the profiler runtime, by default.
 
-> **Important**: If you are building the Rust compiler from the source distribution, the profiler runtime is _not_ enabled in the default `config.toml.example`. Edit your `config.toml` file and ensure the `profiler` feature is set it to `true` (either under the `[build]` section, or under the settings for an individual `[target.<triple>]`):
+> **Important**: If you are building the Rust compiler from the source distribution, the profiler runtime is _not_ enabled in the default `config.example.toml`. Edit your `config.toml` file and ensure the `profiler` feature is set it to `true` (either under the `[build]` section, or under the settings for an individual `[target.<triple>]`):
 >
 > ```toml
 > # Build the profiler runtime (required when compiling with options that depend
diff --git a/src/doc/unstable-book/src/language-features/box-patterns.md b/src/doc/unstable-book/src/language-features/box-patterns.md
index 584f4295cba..a1ac09633b7 100644
--- a/src/doc/unstable-book/src/language-features/box-patterns.md
+++ b/src/doc/unstable-book/src/language-features/box-patterns.md
@@ -4,8 +4,6 @@ The tracking issue for this feature is: [#29641]
 
 [#29641]: https://github.com/rust-lang/rust/issues/29641
 
-See also [`box_syntax`](box-syntax.md)
-
 ------------------------
 
 Box patterns let you match on `Box<T>`s:
diff --git a/src/doc/unstable-book/src/language-features/box-syntax.md b/src/doc/unstable-book/src/language-features/box-syntax.md
deleted file mode 100644
index 9569974d22c..00000000000
--- a/src/doc/unstable-book/src/language-features/box-syntax.md
+++ /dev/null
@@ -1,22 +0,0 @@
-# `box_syntax`
-
-The tracking issue for this feature is: [#49733]
-
-[#49733]: https://github.com/rust-lang/rust/issues/49733
-
-See also [`box_patterns`](box-patterns.md)
-
-------------------------
-
-Currently the only stable way to create a `Box` is via the `Box::new` method.
-Also it is not possible in stable Rust to destructure a `Box` in a match
-pattern. The unstable `box` keyword can be used to create a `Box`. An example
-usage would be:
-
-```rust
-#![feature(box_syntax)]
-
-fn main() {
-    let b = box 5;
-}
-```
diff --git a/src/doc/unstable-book/src/the-unstable-book.md b/src/doc/unstable-book/src/the-unstable-book.md
index 554c52c3c9c..9090b134dc6 100644
--- a/src/doc/unstable-book/src/the-unstable-book.md
+++ b/src/doc/unstable-book/src/the-unstable-book.md
@@ -5,16 +5,31 @@ each one organized by a "feature flag." That is, when using an unstable
 feature of Rust, you must use a flag, like this:
 
 ```rust
-#![feature(box_syntax)]
+#![feature(generators, generator_trait)]
+
+use std::ops::{Generator, GeneratorState};
+use std::pin::Pin;
 
 fn main() {
-    let five = box 5;
+    let mut generator = || {
+        yield 1;
+        return "foo"
+    };
+
+    match Pin::new(&mut generator).resume(()) {
+        GeneratorState::Yielded(1) => {}
+        _ => panic!("unexpected value from resume"),
+    }
+    match Pin::new(&mut generator).resume(()) {
+        GeneratorState::Complete("foo") => {}
+        _ => panic!("unexpected value from resume"),
+    }
 }
 ```
 
-The `box_syntax` feature [has a chapter][box] describing how to use it.
+The `generators` feature [has a chapter][generators] describing how to use it.
 
-[box]: language-features/box-syntax.md
+[generators]: language-features/generators.md
 
 Because this documentation relates to unstable features, we make no guarantees
 that what is contained here is accurate or up to date. It's developed on a
diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs
index 8dbfaf4bbc9..0295de8437e 100644
--- a/src/librustdoc/formats/cache.rs
+++ b/src/librustdoc/formats/cache.rs
@@ -346,6 +346,7 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> {
                                     self.cache,
                                 ),
                                 aliases: item.attrs.get_doc_aliases(),
+                                deprecation: item.deprecation(self.tcx),
                             });
                         }
                     }
diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs
index f9ea829e388..024ea62c31a 100644
--- a/src/librustdoc/html/format.rs
+++ b/src/librustdoc/html/format.rs
@@ -1,13 +1,15 @@
 //! HTML formatting module
 //!
 //! This module contains a large number of `fmt::Display` implementations for
-//! various types in `rustdoc::clean`. These implementations all currently
-//! assume that HTML output is desired, although it may be possible to redesign
-//! them in the future to instead emit any format desired.
+//! various types in `rustdoc::clean`.
+//!
+//! These implementations all emit HTML. As an internal implementation detail,
+//! some of them support an alternate format that emits text, but that should
+//! not be used external to this module.
 
 use std::borrow::Cow;
 use std::cell::Cell;
-use std::fmt;
+use std::fmt::{self, Write};
 use std::iter::{self, once};
 
 use rustc_ast as ast;
@@ -126,7 +128,6 @@ impl Buffer {
     // the fmt::Result return type imposed by fmt::Write (and avoiding the trait
     // import).
     pub(crate) fn write_fmt(&mut self, v: fmt::Arguments<'_>) {
-        use fmt::Write;
         self.buffer.write_fmt(v).unwrap();
     }
 
@@ -279,8 +280,6 @@ pub(crate) fn print_where_clause<'a, 'tcx: 'a>(
     indent: usize,
     ending: Ending,
 ) -> impl fmt::Display + 'a + Captures<'tcx> {
-    use fmt::Write;
-
     display_fn(move |f| {
         let mut where_predicates = gens.where_predicates.iter().filter(|pred| {
             !matches!(pred, clean::WherePredicate::BoundPredicate { bounds, .. } if bounds.is_empty())
@@ -1306,6 +1305,28 @@ impl clean::BareFunctionDecl {
     }
 }
 
+// Implements Write but only counts the bytes "written".
+struct WriteCounter(usize);
+
+impl std::fmt::Write for WriteCounter {
+    fn write_str(&mut self, s: &str) -> fmt::Result {
+        self.0 += s.len();
+        Ok(())
+    }
+}
+
+// Implements Display by emitting the given number of spaces.
+struct Indent(usize);
+
+impl fmt::Display for Indent {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        (0..self.0).for_each(|_| {
+            f.write_char(' ').unwrap();
+        });
+        Ok(())
+    }
+}
+
 impl clean::FnDecl {
     pub(crate) fn print<'b, 'a: 'b, 'tcx: 'a>(
         &'a self,
@@ -1345,95 +1366,80 @@ impl clean::FnDecl {
         indent: usize,
         cx: &'a Context<'tcx>,
     ) -> impl fmt::Display + 'a + Captures<'tcx> {
-        display_fn(move |f| self.inner_full_print(header_len, indent, f, cx))
+        display_fn(move |f| {
+            // First, generate the text form of the declaration, with no line wrapping, and count the bytes.
+            let mut counter = WriteCounter(0);
+            write!(&mut counter, "{:#}", display_fn(|f| { self.inner_full_print(None, f, cx) }))
+                .unwrap();
+            // If the text form was over 80 characters wide, we will line-wrap our output.
+            let line_wrapping_indent =
+                if header_len + counter.0 > 80 { Some(indent) } else { None };
+            // Generate the final output. This happens to accept `{:#}` formatting to get textual
+            // output but in practice it is only formatted with `{}` to get HTML output.
+            self.inner_full_print(line_wrapping_indent, f, cx)
+        })
     }
 
     fn inner_full_print(
         &self,
-        header_len: usize,
-        indent: usize,
+        // For None, the declaration will not be line-wrapped. For Some(n),
+        // the declaration will be line-wrapped, with an indent of n spaces.
+        line_wrapping_indent: Option<usize>,
         f: &mut fmt::Formatter<'_>,
         cx: &Context<'_>,
     ) -> fmt::Result {
         let amp = if f.alternate() { "&" } else { "&amp;" };
-        let mut args = Buffer::html();
-        let mut args_plain = Buffer::new();
+
+        write!(f, "(")?;
+        if let Some(n) = line_wrapping_indent {
+            write!(f, "\n{}", Indent(n + 4))?;
+        }
         for (i, input) in self.inputs.values.iter().enumerate() {
+            if i > 0 {
+                match line_wrapping_indent {
+                    None => write!(f, ", ")?,
+                    Some(n) => write!(f, ",\n{}", Indent(n + 4))?,
+                };
+            }
             if let Some(selfty) = input.to_self() {
                 match selfty {
                     clean::SelfValue => {
-                        args.push_str("self");
-                        args_plain.push_str("self");
+                        write!(f, "self")?;
                     }
                     clean::SelfBorrowed(Some(ref lt), mtbl) => {
-                        write!(args, "{}{} {}self", amp, lt.print(), mtbl.print_with_space());
-                        write!(args_plain, "&{} {}self", lt.print(), mtbl.print_with_space());
+                        write!(f, "{}{} {}self", amp, lt.print(), mtbl.print_with_space())?;
                     }
                     clean::SelfBorrowed(None, mtbl) => {
-                        write!(args, "{}{}self", amp, mtbl.print_with_space());
-                        write!(args_plain, "&{}self", mtbl.print_with_space());
+                        write!(f, "{}{}self", amp, mtbl.print_with_space())?;
                     }
                     clean::SelfExplicit(ref typ) => {
-                        if f.alternate() {
-                            write!(args, "self: {:#}", typ.print(cx));
-                        } else {
-                            write!(args, "self: {}", typ.print(cx));
-                        }
-                        write!(args_plain, "self: {:#}", typ.print(cx));
+                        write!(f, "self: ")?;
+                        fmt::Display::fmt(&typ.print(cx), f)?;
                     }
                 }
             } else {
-                if i > 0 {
-                    args.push_str("\n");
-                }
                 if input.is_const {
-                    args.push_str("const ");
-                    args_plain.push_str("const ");
-                }
-                write!(args, "{}: ", input.name);
-                write!(args_plain, "{}: ", input.name);
-
-                if f.alternate() {
-                    write!(args, "{:#}", input.type_.print(cx));
-                } else {
-                    write!(args, "{}", input.type_.print(cx));
+                    write!(f, "const ")?;
                 }
-                write!(args_plain, "{:#}", input.type_.print(cx));
-            }
-            if i + 1 < self.inputs.values.len() {
-                args.push_str(",");
-                args_plain.push_str(",");
+                write!(f, "{}: ", input.name)?;
+                fmt::Display::fmt(&input.type_.print(cx), f)?;
             }
         }
 
-        let mut args_plain = format!("({})", args_plain.into_inner());
-        let mut args = args.into_inner();
-
         if self.c_variadic {
-            args.push_str(",\n ...");
-            args_plain.push_str(", ...");
+            match line_wrapping_indent {
+                None => write!(f, ", ...")?,
+                Some(n) => write!(f, "\n{}...", Indent(n + 4))?,
+            };
         }
 
-        let arrow_plain = format!("{:#}", self.output.print(cx));
-        let arrow =
-            if f.alternate() { arrow_plain.clone() } else { format!("{}", self.output.print(cx)) };
-
-        let declaration_len = header_len + args_plain.len() + arrow_plain.len();
-        let output = if declaration_len > 80 {
-            let full_pad = format!("\n{}", " ".repeat(indent + 4));
-            let close_pad = format!("\n{}", " ".repeat(indent));
-            format!(
-                "({pad}{args}{close}){arrow}",
-                pad = if self.inputs.values.is_empty() { "" } else { &full_pad },
-                args = args.replace('\n', &full_pad),
-                close = close_pad,
-                arrow = arrow
-            )
-        } else {
-            format!("({args}){arrow}", args = args.replace('\n', " "), arrow = arrow)
+        match line_wrapping_indent {
+            None => write!(f, ")")?,
+            Some(n) => write!(f, "\n{})", Indent(n))?,
         };
 
-        write!(f, "{}", output)
+        fmt::Display::fmt(&self.output.print(cx), f)?;
+        Ok(())
     }
 }
 
diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs
index ed1eb66b97c..63cd0e04a28 100644
--- a/src/librustdoc/html/render/context.rs
+++ b/src/librustdoc/html/render/context.rs
@@ -17,10 +17,11 @@ use super::print_item::{full_path, item_path, print_item};
 use super::search_index::build_index;
 use super::write_shared::write_shared;
 use super::{
-    collect_spans_and_sources, print_sidebar, scrape_examples_help, sidebar_module_like, AllTypes,
-    LinkFromSrc, StylePath,
+    collect_spans_and_sources, scrape_examples_help,
+    sidebar::print_sidebar,
+    sidebar::{sidebar_module_like, Sidebar},
+    AllTypes, LinkFromSrc, StylePath,
 };
-
 use crate::clean::{self, types::ExternalLocation, ExternalCrate};
 use crate::config::{ModuleSorting, RenderOptions};
 use crate::docfs::{DocFS, PathError};
@@ -35,6 +36,7 @@ use crate::html::url_parts_builder::UrlPartsBuilder;
 use crate::html::{layout, sources, static_files};
 use crate::scrape_examples::AllCallLocations;
 use crate::try_err;
+use askama::Template;
 
 /// Major driving force in all rustdoc rendering. This contains information
 /// about where in the tree-like hierarchy rendering is occurring and controls
@@ -600,15 +602,18 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
         };
         let all = shared.all.replace(AllTypes::new());
         let mut sidebar = Buffer::html();
-        write!(sidebar, "<h2 class=\"location\"><a href=\"#\">Crate {}</a></h2>", crate_name);
-
-        let mut items = Buffer::html();
-        sidebar_module_like(&mut items, all.item_sections());
-        if !items.is_empty() {
-            sidebar.push_str("<div class=\"sidebar-elems\">");
-            sidebar.push_buffer(items);
-            sidebar.push_str("</div>");
-        }
+
+        let blocks = sidebar_module_like(all.item_sections());
+        let bar = Sidebar {
+            title_prefix: "Crate ",
+            title: crate_name.as_str(),
+            is_crate: false,
+            version: "",
+            blocks: vec![blocks],
+            path: String::new(),
+        };
+
+        bar.render_into(&mut sidebar).unwrap();
 
         let v = layout::render(
             &shared.layout,
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index e6a040d02e5..da1f1cf5ecc 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -30,6 +30,7 @@ mod tests;
 
 mod context;
 mod print_item;
+mod sidebar;
 mod span_map;
 mod write_shared;
 
@@ -46,14 +47,13 @@ use std::rc::Rc;
 use std::str;
 use std::string::ToString;
 
+use askama::Template;
 use rustc_ast_pretty::pprust;
 use rustc_attr::{ConstStability, Deprecation, StabilityLevel};
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_hir::def::CtorKind;
 use rustc_hir::def_id::{DefId, DefIdSet};
 use rustc_hir::Mutability;
 use rustc_middle::middle::stability;
-use rustc_middle::ty;
 use rustc_middle::ty::TyCtxt;
 use rustc_span::{
     symbol::{sym, Symbol},
@@ -104,6 +104,7 @@ pub(crate) struct IndexItem {
     pub(crate) parent_idx: Option<usize>,
     pub(crate) search_type: Option<IndexItemFunctionType>,
     pub(crate) aliases: Box<[Symbol]>,
+    pub(crate) deprecation: Option<Deprecation>,
 }
 
 /// A type used for the search index.
@@ -417,7 +418,7 @@ fn document(
     if let Some(ref name) = item.name {
         info!("Documenting {}", name);
     }
-    document_item_info(w, cx, item, parent);
+    document_item_info(cx, item, parent).render_into(w).unwrap();
     if parent.is_none() {
         document_full_collapsible(w, item, cx, heading_offset);
     } else {
@@ -459,7 +460,7 @@ fn document_short(
     parent: &clean::Item,
     show_def_docs: bool,
 ) {
-    document_item_info(w, cx, item, Some(parent));
+    document_item_info(cx, item, Some(parent)).render_into(w).unwrap();
     if !show_def_docs {
         return;
     }
@@ -531,25 +532,23 @@ fn document_full_inner(
     }
 }
 
+#[derive(Template)]
+#[template(path = "item_info.html")]
+struct ItemInfo {
+    items: Vec<ShortItemInfo>,
+}
 /// Add extra information about an item such as:
 ///
 /// * Stability
 /// * Deprecated
 /// * Required features (through the `doc_cfg` feature)
 fn document_item_info(
-    w: &mut Buffer,
     cx: &mut Context<'_>,
     item: &clean::Item,
     parent: Option<&clean::Item>,
-) {
-    let item_infos = short_item_info(item, cx, parent);
-    if !item_infos.is_empty() {
-        w.write_str("<span class=\"item-info\">");
-        for info in item_infos {
-            w.write_str(&info);
-        }
-        w.write_str("</span>");
-    }
+) -> ItemInfo {
+    let items = short_item_info(item, cx, parent);
+    ItemInfo { items }
 }
 
 fn portability(item: &clean::Item, parent: Option<&clean::Item>) -> Option<String> {
@@ -567,7 +566,25 @@ fn portability(item: &clean::Item, parent: Option<&clean::Item>) -> Option<Strin
         cfg
     );
 
-    Some(format!("<div class=\"stab portability\">{}</div>", cfg?.render_long_html()))
+    Some(cfg?.render_long_html())
+}
+
+#[derive(Template)]
+#[template(path = "short_item_info.html")]
+enum ShortItemInfo {
+    /// A message describing the deprecation of this item
+    Deprecation {
+        message: String,
+    },
+    /// The feature corresponding to an unstable item, and optionally
+    /// a tracking issue URL and number.
+    Unstable {
+        feature: String,
+        tracking: Option<(String, u32)>,
+    },
+    Portability {
+        message: String,
+    },
 }
 
 /// Render the stability, deprecation and portability information that is displayed at the top of
@@ -576,7 +593,7 @@ fn short_item_info(
     item: &clean::Item,
     cx: &mut Context<'_>,
     parent: Option<&clean::Item>,
-) -> Vec<String> {
+) -> Vec<ShortItemInfo> {
     let mut extra_info = vec![];
 
     if let Some(depr @ Deprecation { note, since, is_since_rustc_version: _, suggestion: _ }) =
@@ -602,15 +619,10 @@ fn short_item_info(
         if let Some(note) = note {
             let note = note.as_str();
             let html = MarkdownItemInfo(note, &mut cx.id_map);
-            message.push_str(&format!(": {}", html.into_string()));
-        }
-        extra_info.push(format!(
-            "<div class=\"stab deprecated\">\
-                 <span class=\"emoji\">👎</span>\
-                 <span>{}</span>\
-             </div>",
-            message,
-        ));
+            message.push_str(": ");
+            message.push_str(&html.into_string());
+        }
+        extra_info.push(ShortItemInfo::Deprecation { message });
     }
 
     // Render unstable items. But don't render "rustc_private" crates (internal compiler crates).
@@ -621,26 +633,17 @@ fn short_item_info(
         .filter(|stab| stab.feature != sym::rustc_private)
         .map(|stab| (stab.level, stab.feature))
     {
-        let mut message = "<span class=\"emoji\">🔬</span>\
-             <span>This is a nightly-only experimental API."
-            .to_owned();
-
-        let mut feature = format!("<code>{}</code>", Escape(feature.as_str()));
-        if let (Some(url), Some(issue)) = (&cx.shared.issue_tracker_base_url, issue) {
-            feature.push_str(&format!(
-                "&nbsp;<a href=\"{url}{issue}\">#{issue}</a>",
-                url = url,
-                issue = issue
-            ));
-        }
-
-        message.push_str(&format!(" ({})</span>", feature));
-
-        extra_info.push(format!("<div class=\"stab unstable\">{}</div>", message));
+        let tracking = if let (Some(url), Some(issue)) = (&cx.shared.issue_tracker_base_url, issue)
+        {
+            Some((url.clone(), issue.get()))
+        } else {
+            None
+        };
+        extra_info.push(ShortItemInfo::Unstable { feature: feature.to_string(), tracking });
     }
 
-    if let Some(portability) = portability(item, parent) {
-        extra_info.push(portability);
+    if let Some(message) = portability(item, parent) {
+        extra_info.push(ShortItemInfo::Portability { message });
     }
 
     extra_info
@@ -1472,7 +1475,9 @@ fn render_impl(
                         // We need the stability of the item from the trait
                         // because impls can't have a stability.
                         if item.doc_value().is_some() {
-                            document_item_info(&mut info_buffer, cx, it, Some(parent));
+                            document_item_info(cx, it, Some(parent))
+                                .render_into(&mut info_buffer)
+                                .unwrap();
                             document_full(&mut doc_buffer, item, cx, HeadingOffset::H5);
                             short_documented = false;
                         } else {
@@ -1489,7 +1494,9 @@ fn render_impl(
                         }
                     }
                 } else {
-                    document_item_info(&mut info_buffer, cx, item, Some(parent));
+                    document_item_info(cx, item, Some(parent))
+                        .render_into(&mut info_buffer)
+                        .unwrap();
                     if rendering_params.show_def_docs {
                         document_full(&mut doc_buffer, item, cx, HeadingOffset::H5);
                         short_documented = false;
@@ -1862,161 +1869,17 @@ pub(crate) fn render_impl_summary(
     let is_trait = inner_impl.trait_.is_some();
     if is_trait {
         if let Some(portability) = portability(&i.impl_item, Some(parent)) {
-            write!(w, "<span class=\"item-info\">{}</span>", portability);
+            write!(
+                w,
+                "<span class=\"item-info\"><div class=\"stab portability\">{}</div></span>",
+                portability
+            );
         }
     }
 
     w.write_str("</section>");
 }
 
-fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buffer) {
-    if it.is_struct()
-        || it.is_trait()
-        || it.is_primitive()
-        || it.is_union()
-        || it.is_enum()
-        || it.is_mod()
-        || it.is_typedef()
-    {
-        write!(
-            buffer,
-            "<h2 class=\"location\"><a href=\"#\">{}{}</a></h2>",
-            match *it.kind {
-                clean::ModuleItem(..) =>
-                    if it.is_crate() {
-                        "Crate "
-                    } else {
-                        "Module "
-                    },
-                _ => "",
-            },
-            it.name.as_ref().unwrap()
-        );
-    }
-
-    buffer.write_str("<div class=\"sidebar-elems\">");
-    if it.is_crate() {
-        write!(buffer, "<ul class=\"block\">");
-        if let Some(ref version) = cx.cache().crate_version {
-            write!(buffer, "<li class=\"version\">Version {}</li>", Escape(version));
-        }
-        write!(buffer, "<li><a id=\"all-types\" href=\"all.html\">All Items</a></li>");
-        buffer.write_str("</ul>");
-    }
-
-    match *it.kind {
-        clean::StructItem(ref s) => sidebar_struct(cx, buffer, it, s),
-        clean::TraitItem(ref t) => sidebar_trait(cx, buffer, it, t),
-        clean::PrimitiveItem(_) => sidebar_primitive(cx, buffer, it),
-        clean::UnionItem(ref u) => sidebar_union(cx, buffer, it, u),
-        clean::EnumItem(ref e) => sidebar_enum(cx, buffer, it, e),
-        clean::TypedefItem(_) => sidebar_typedef(cx, buffer, it),
-        clean::ModuleItem(ref m) => sidebar_module(buffer, &m.items),
-        clean::ForeignTypeItem => sidebar_foreign_type(cx, buffer, it),
-        _ => {}
-    }
-
-    // The sidebar is designed to display sibling functions, modules and
-    // other miscellaneous information. since there are lots of sibling
-    // items (and that causes quadratic growth in large modules),
-    // we refactor common parts into a shared JavaScript file per module.
-    // still, we don't move everything into JS because we want to preserve
-    // as much HTML as possible in order to allow non-JS-enabled browsers
-    // to navigate the documentation (though slightly inefficiently).
-
-    if !it.is_mod() {
-        let path: String = cx.current.iter().map(|s| s.as_str()).intersperse("::").collect();
-
-        write!(buffer, "<h2><a href=\"index.html\">In {}</a></h2>", path);
-    }
-
-    // Closes sidebar-elems div.
-    buffer.write_str("</div>");
-}
-
-fn get_next_url(used_links: &mut FxHashSet<String>, url: String) -> String {
-    if used_links.insert(url.clone()) {
-        return url;
-    }
-    let mut add = 1;
-    while !used_links.insert(format!("{}-{}", url, add)) {
-        add += 1;
-    }
-    format!("{}-{}", url, add)
-}
-
-struct SidebarLink {
-    name: Symbol,
-    url: String,
-}
-
-impl fmt::Display for SidebarLink {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(f, "<a href=\"#{}\">{}</a>", self.url, self.name)
-    }
-}
-
-impl PartialEq for SidebarLink {
-    fn eq(&self, other: &Self) -> bool {
-        self.url == other.url
-    }
-}
-
-impl Eq for SidebarLink {}
-
-impl PartialOrd for SidebarLink {
-    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
-        Some(self.cmp(other))
-    }
-}
-
-impl Ord for SidebarLink {
-    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
-        self.url.cmp(&other.url)
-    }
-}
-
-fn get_methods(
-    i: &clean::Impl,
-    for_deref: bool,
-    used_links: &mut FxHashSet<String>,
-    deref_mut: bool,
-    tcx: TyCtxt<'_>,
-) -> Vec<SidebarLink> {
-    i.items
-        .iter()
-        .filter_map(|item| match item.name {
-            Some(name) if !name.is_empty() && item.is_method() => {
-                if !for_deref || should_render_item(item, deref_mut, tcx) {
-                    Some(SidebarLink {
-                        name,
-                        url: get_next_url(used_links, format!("{}.{}", ItemType::Method, name)),
-                    })
-                } else {
-                    None
-                }
-            }
-            _ => None,
-        })
-        .collect::<Vec<_>>()
-}
-
-fn get_associated_constants(
-    i: &clean::Impl,
-    used_links: &mut FxHashSet<String>,
-) -> Vec<SidebarLink> {
-    i.items
-        .iter()
-        .filter_map(|item| match item.name {
-            Some(name) if !name.is_empty() && item.is_associated_const() => Some(SidebarLink {
-                name,
-                url: get_next_url(used_links, format!("{}.{}", ItemType::AssocConst, name)),
-            }),
-            _ => None,
-        })
-        .collect::<Vec<_>>()
-}
-
 pub(crate) fn small_url_encode(s: String) -> String {
     // These characters don't need to be escaped in a URI.
     // See https://url.spec.whatwg.org/#query-percent-encode-set
@@ -2082,232 +1945,6 @@ pub(crate) fn small_url_encode(s: String) -> String {
     }
 }
 
-pub(crate) fn sidebar_render_assoc_items(
-    cx: &Context<'_>,
-    out: &mut Buffer,
-    id_map: &mut IdMap,
-    concrete: Vec<&Impl>,
-    synthetic: Vec<&Impl>,
-    blanket_impl: Vec<&Impl>,
-) {
-    let format_impls = |impls: Vec<&Impl>, id_map: &mut IdMap| {
-        let mut links = FxHashSet::default();
-
-        let mut ret = impls
-            .iter()
-            .filter_map(|it| {
-                let trait_ = it.inner_impl().trait_.as_ref()?;
-                let encoded =
-                    id_map.derive(get_id_for_impl(&it.inner_impl().for_, Some(trait_), cx));
-
-                let i_display = format!("{:#}", trait_.print(cx));
-                let out = Escape(&i_display);
-                let prefix = match it.inner_impl().polarity {
-                    ty::ImplPolarity::Positive | ty::ImplPolarity::Reservation => "",
-                    ty::ImplPolarity::Negative => "!",
-                };
-                let generated = format!("<a href=\"#{}\">{}{}</a>", encoded, prefix, out);
-                if links.insert(generated.clone()) { Some(generated) } else { None }
-            })
-            .collect::<Vec<String>>();
-        ret.sort();
-        ret
-    };
-
-    let concrete_format = format_impls(concrete, id_map);
-    let synthetic_format = format_impls(synthetic, id_map);
-    let blanket_format = format_impls(blanket_impl, id_map);
-
-    if !concrete_format.is_empty() {
-        print_sidebar_block(
-            out,
-            "trait-implementations",
-            "Trait Implementations",
-            concrete_format.iter(),
-        );
-    }
-
-    if !synthetic_format.is_empty() {
-        print_sidebar_block(
-            out,
-            "synthetic-implementations",
-            "Auto Trait Implementations",
-            synthetic_format.iter(),
-        );
-    }
-
-    if !blanket_format.is_empty() {
-        print_sidebar_block(
-            out,
-            "blanket-implementations",
-            "Blanket Implementations",
-            blanket_format.iter(),
-        );
-    }
-}
-
-fn sidebar_assoc_items(cx: &Context<'_>, out: &mut Buffer, it: &clean::Item) {
-    let did = it.item_id.expect_def_id();
-    let cache = cx.cache();
-
-    if let Some(v) = cache.impls.get(&did) {
-        let mut used_links = FxHashSet::default();
-        let mut id_map = IdMap::new();
-
-        {
-            let used_links_bor = &mut used_links;
-            let mut assoc_consts = v
-                .iter()
-                .filter(|i| i.inner_impl().trait_.is_none())
-                .flat_map(|i| get_associated_constants(i.inner_impl(), used_links_bor))
-                .collect::<Vec<_>>();
-            if !assoc_consts.is_empty() {
-                // We want links' order to be reproducible so we don't use unstable sort.
-                assoc_consts.sort();
-
-                print_sidebar_block(
-                    out,
-                    "implementations",
-                    "Associated Constants",
-                    assoc_consts.iter(),
-                );
-            }
-            let mut methods = v
-                .iter()
-                .filter(|i| i.inner_impl().trait_.is_none())
-                .flat_map(|i| get_methods(i.inner_impl(), false, used_links_bor, false, cx.tcx()))
-                .collect::<Vec<_>>();
-            if !methods.is_empty() {
-                // We want links' order to be reproducible so we don't use unstable sort.
-                methods.sort();
-
-                print_sidebar_block(out, "implementations", "Methods", methods.iter());
-            }
-        }
-
-        if v.iter().any(|i| i.inner_impl().trait_.is_some()) {
-            if let Some(impl_) =
-                v.iter().find(|i| i.trait_did() == cx.tcx().lang_items().deref_trait())
-            {
-                let mut derefs = DefIdSet::default();
-                derefs.insert(did);
-                sidebar_deref_methods(cx, out, impl_, v, &mut derefs, &mut used_links);
-            }
-
-            let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) =
-                v.iter().partition::<Vec<_>, _>(|i| i.inner_impl().kind.is_auto());
-            let (blanket_impl, concrete): (Vec<&Impl>, Vec<&Impl>) =
-                concrete.into_iter().partition::<Vec<_>, _>(|i| i.inner_impl().kind.is_blanket());
-
-            sidebar_render_assoc_items(cx, out, &mut id_map, concrete, synthetic, blanket_impl);
-        }
-    }
-}
-
-fn sidebar_deref_methods(
-    cx: &Context<'_>,
-    out: &mut Buffer,
-    impl_: &Impl,
-    v: &[Impl],
-    derefs: &mut DefIdSet,
-    used_links: &mut FxHashSet<String>,
-) {
-    let c = cx.cache();
-
-    debug!("found Deref: {:?}", impl_);
-    if let Some((target, real_target)) =
-        impl_.inner_impl().items.iter().find_map(|item| match *item.kind {
-            clean::AssocTypeItem(box ref t, _) => Some(match *t {
-                clean::Typedef { item_type: Some(ref type_), .. } => (type_, &t.type_),
-                _ => (&t.type_, &t.type_),
-            }),
-            _ => None,
-        })
-    {
-        debug!("found target, real_target: {:?} {:?}", target, real_target);
-        if let Some(did) = target.def_id(c) &&
-            let Some(type_did) = impl_.inner_impl().for_.def_id(c) &&
-            // `impl Deref<Target = S> for S`
-            (did == type_did || !derefs.insert(did))
-        {
-            // Avoid infinite cycles
-            return;
-        }
-        let deref_mut = v.iter().any(|i| i.trait_did() == cx.tcx().lang_items().deref_mut_trait());
-        let inner_impl = target
-            .def_id(c)
-            .or_else(|| {
-                target.primitive_type().and_then(|prim| c.primitive_locations.get(&prim).cloned())
-            })
-            .and_then(|did| c.impls.get(&did));
-        if let Some(impls) = inner_impl {
-            debug!("found inner_impl: {:?}", impls);
-            let mut ret = impls
-                .iter()
-                .filter(|i| i.inner_impl().trait_.is_none())
-                .flat_map(|i| get_methods(i.inner_impl(), true, used_links, deref_mut, cx.tcx()))
-                .collect::<Vec<_>>();
-            if !ret.is_empty() {
-                let id = if let Some(target_def_id) = real_target.def_id(c) {
-                    cx.deref_id_map.get(&target_def_id).expect("Deref section without derived id")
-                } else {
-                    "deref-methods"
-                };
-                let title = format!(
-                    "Methods from {}&lt;Target={}&gt;",
-                    Escape(&format!("{:#}", impl_.inner_impl().trait_.as_ref().unwrap().print(cx))),
-                    Escape(&format!("{:#}", real_target.print(cx))),
-                );
-                // We want links' order to be reproducible so we don't use unstable sort.
-                ret.sort();
-                print_sidebar_block(out, id, &title, ret.iter());
-            }
-        }
-
-        // Recurse into any further impls that might exist for `target`
-        if let Some(target_did) = target.def_id(c) &&
-            let Some(target_impls) = c.impls.get(&target_did) &&
-            let Some(target_deref_impl) = target_impls.iter().find(|i| {
-                i.inner_impl()
-                    .trait_
-                    .as_ref()
-                    .map(|t| Some(t.def_id()) == cx.tcx().lang_items().deref_trait())
-                    .unwrap_or(false)
-            })
-        {
-            sidebar_deref_methods(
-                cx,
-                out,
-                target_deref_impl,
-                target_impls,
-                derefs,
-                used_links,
-            );
-        }
-    }
-}
-
-fn sidebar_struct(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, s: &clean::Struct) {
-    let mut sidebar = Buffer::new();
-    let fields = get_struct_fields_name(&s.fields);
-
-    if !fields.is_empty() {
-        match s.ctor_kind {
-            None => {
-                print_sidebar_block(&mut sidebar, "fields", "Fields", fields.iter());
-            }
-            Some(CtorKind::Fn) => print_sidebar_title(&mut sidebar, "fields", "Tuple Fields"),
-            Some(CtorKind::Const) => {}
-        }
-    }
-
-    sidebar_assoc_items(cx, &mut sidebar, it);
-
-    if !sidebar.is_empty() {
-        write!(buf, "<section>{}</section>", sidebar.into_inner());
-    }
-}
-
 fn get_id_for_impl(for_: &clean::Type, trait_: Option<&clean::Path>, cx: &Context<'_>) -> String {
     match trait_ {
         Some(t) => small_url_encode(format!("impl-{:#}-for-{:#}", t.print(cx), for_.print(cx))),
@@ -2328,131 +1965,6 @@ fn extract_for_impl_name(item: &clean::Item, cx: &Context<'_>) -> Option<(String
     }
 }
 
-fn print_sidebar_title(buf: &mut Buffer, id: &str, title: &str) {
-    write!(buf, "<h3><a href=\"#{}\">{}</a></h3>", id, title);
-}
-
-fn print_sidebar_block(
-    buf: &mut Buffer,
-    id: &str,
-    title: &str,
-    items: impl Iterator<Item = impl fmt::Display>,
-) {
-    print_sidebar_title(buf, id, title);
-    buf.push_str("<ul class=\"block\">");
-    for item in items {
-        write!(buf, "<li>{}</li>", item);
-    }
-    buf.push_str("</ul>");
-}
-
-fn sidebar_trait(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, t: &clean::Trait) {
-    buf.write_str("<section>");
-
-    fn print_sidebar_section(
-        out: &mut Buffer,
-        items: &[clean::Item],
-        id: &str,
-        title: &str,
-        filter: impl Fn(&clean::Item) -> bool,
-        mapper: impl Fn(&str) -> String,
-    ) {
-        let mut items: Vec<&str> = items
-            .iter()
-            .filter_map(|m| match m.name {
-                Some(ref name) if filter(m) => Some(name.as_str()),
-                _ => None,
-            })
-            .collect::<Vec<_>>();
-
-        if !items.is_empty() {
-            items.sort_unstable();
-            print_sidebar_block(out, id, title, items.into_iter().map(mapper));
-        }
-    }
-
-    print_sidebar_section(
-        buf,
-        &t.items,
-        "required-associated-types",
-        "Required Associated Types",
-        |m| m.is_ty_associated_type(),
-        |sym| format!("<a href=\"#{1}.{0}\">{0}</a>", sym, ItemType::AssocType),
-    );
-
-    print_sidebar_section(
-        buf,
-        &t.items,
-        "provided-associated-types",
-        "Provided Associated Types",
-        |m| m.is_associated_type(),
-        |sym| format!("<a href=\"#{1}.{0}\">{0}</a>", sym, ItemType::AssocType),
-    );
-
-    print_sidebar_section(
-        buf,
-        &t.items,
-        "required-associated-consts",
-        "Required Associated Constants",
-        |m| m.is_ty_associated_const(),
-        |sym| format!("<a href=\"#{1}.{0}\">{0}</a>", sym, ItemType::AssocConst),
-    );
-
-    print_sidebar_section(
-        buf,
-        &t.items,
-        "provided-associated-consts",
-        "Provided Associated Constants",
-        |m| m.is_associated_const(),
-        |sym| format!("<a href=\"#{1}.{0}\">{0}</a>", sym, ItemType::AssocConst),
-    );
-
-    print_sidebar_section(
-        buf,
-        &t.items,
-        "required-methods",
-        "Required Methods",
-        |m| m.is_ty_method(),
-        |sym| format!("<a href=\"#{1}.{0}\">{0}</a>", sym, ItemType::TyMethod),
-    );
-
-    print_sidebar_section(
-        buf,
-        &t.items,
-        "provided-methods",
-        "Provided Methods",
-        |m| m.is_method(),
-        |sym| format!("<a href=\"#{1}.{0}\">{0}</a>", sym, ItemType::Method),
-    );
-
-    if let Some(implementors) = cx.cache().implementors.get(&it.item_id.expect_def_id()) {
-        let mut res = implementors
-            .iter()
-            .filter(|i| !i.is_on_local_type(cx))
-            .filter_map(|i| extract_for_impl_name(&i.impl_item, cx))
-            .collect::<Vec<_>>();
-
-        if !res.is_empty() {
-            res.sort();
-            print_sidebar_block(
-                buf,
-                "foreign-impls",
-                "Implementations on Foreign Types",
-                res.iter().map(|(name, id)| format!("<a href=\"#{}\">{}</a>", id, Escape(name))),
-            );
-        }
-    }
-
-    sidebar_assoc_items(cx, buf, it);
-
-    print_sidebar_title(buf, "implementors", "Implementors");
-    if t.is_auto(cx.tcx()) {
-        print_sidebar_title(buf, "synthetic-implementors", "Auto Implementors");
-    }
-
-    buf.push_str("</section>")
-}
-
 /// Returns the list of implementations for the primitive reference type, filtering out any
 /// implementations that are on concrete or partially generic types, only keeping implementations
 /// of the form `impl<T> Trait for &T`.
@@ -2483,89 +1995,6 @@ pub(crate) fn get_filtered_impls_for_reference<'a>(
     (concrete, synthetic, blanket_impl)
 }
 
-fn sidebar_primitive(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item) {
-    let mut sidebar = Buffer::new();
-
-    if it.name.map(|n| n.as_str() != "reference").unwrap_or(false) {
-        sidebar_assoc_items(cx, &mut sidebar, it);
-    } else {
-        let shared = Rc::clone(&cx.shared);
-        let (concrete, synthetic, blanket_impl) = get_filtered_impls_for_reference(&shared, it);
-
-        sidebar_render_assoc_items(
-            cx,
-            &mut sidebar,
-            &mut IdMap::new(),
-            concrete,
-            synthetic,
-            blanket_impl,
-        );
-    }
-
-    if !sidebar.is_empty() {
-        write!(buf, "<section>{}</section>", sidebar.into_inner());
-    }
-}
-
-fn sidebar_typedef(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item) {
-    let mut sidebar = Buffer::new();
-    sidebar_assoc_items(cx, &mut sidebar, it);
-
-    if !sidebar.is_empty() {
-        write!(buf, "<section>{}</section>", sidebar.into_inner());
-    }
-}
-
-fn get_struct_fields_name(fields: &[clean::Item]) -> Vec<String> {
-    let mut fields = fields
-        .iter()
-        .filter(|f| matches!(*f.kind, clean::StructFieldItem(..)))
-        .filter_map(|f| {
-            f.name.map(|name| format!("<a href=\"#structfield.{name}\">{name}</a>", name = name))
-        })
-        .collect::<Vec<_>>();
-    fields.sort();
-    fields
-}
-
-fn sidebar_union(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, u: &clean::Union) {
-    let mut sidebar = Buffer::new();
-    let fields = get_struct_fields_name(&u.fields);
-
-    if !fields.is_empty() {
-        print_sidebar_block(&mut sidebar, "fields", "Fields", fields.iter());
-    }
-
-    sidebar_assoc_items(cx, &mut sidebar, it);
-
-    if !sidebar.is_empty() {
-        write!(buf, "<section>{}</section>", sidebar.into_inner());
-    }
-}
-
-fn sidebar_enum(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, e: &clean::Enum) {
-    let mut sidebar = Buffer::new();
-
-    let mut variants = e
-        .variants()
-        .filter_map(|v| {
-            v.name
-                .as_ref()
-                .map(|name| format!("<a href=\"#variant.{name}\">{name}</a>", name = name))
-        })
-        .collect::<Vec<_>>();
-    if !variants.is_empty() {
-        variants.sort_unstable();
-        print_sidebar_block(&mut sidebar, "variants", "Variants", variants.iter());
-    }
-
-    sidebar_assoc_items(cx, &mut sidebar, it);
-
-    if !sidebar.is_empty() {
-        write!(buf, "<section>{}</section>", sidebar.into_inner());
-    }
-}
-
 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
 pub(crate) enum ItemSection {
     Reexports,
@@ -2719,54 +2148,6 @@ fn item_ty_to_section(ty: ItemType) -> ItemSection {
     }
 }
 
-pub(crate) fn sidebar_module_like(buf: &mut Buffer, item_sections_in_use: FxHashSet<ItemSection>) {
-    use std::fmt::Write as _;
-
-    let mut sidebar = String::new();
-
-    for &sec in ItemSection::ALL.iter().filter(|sec| item_sections_in_use.contains(sec)) {
-        let _ = write!(sidebar, "<li><a href=\"#{}\">{}</a></li>", sec.id(), sec.name());
-    }
-
-    if !sidebar.is_empty() {
-        write!(
-            buf,
-            "<section>\
-                 <ul class=\"block\">{}</ul>\
-             </section>",
-            sidebar
-        );
-    }
-}
-
-fn sidebar_module(buf: &mut Buffer, items: &[clean::Item]) {
-    let item_sections_in_use: FxHashSet<_> = items
-        .iter()
-        .filter(|it| {
-            !it.is_stripped()
-                && it
-                    .name
-                    .or_else(|| {
-                        if let clean::ImportItem(ref i) = *it.kind &&
-                            let clean::ImportKind::Simple(s) = i.kind { Some(s) } else { None }
-                    })
-                    .is_some()
-        })
-        .map(|it| item_ty_to_section(it.type_()))
-        .collect();
-
-    sidebar_module_like(buf, item_sections_in_use);
-}
-
-fn sidebar_foreign_type(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item) {
-    let mut sidebar = Buffer::new();
-    sidebar_assoc_items(cx, &mut sidebar, it);
-
-    if !sidebar.is_empty() {
-        write!(buf, "<section>{}</section>", sidebar.into_inner());
-    }
-}
-
 /// Returns a list of all paths used in the type.
 /// This is used to help deduplicate imported impls
 /// for reexported types. If any of the contained
diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs
index 08796f10d92..577497868f6 100644
--- a/src/librustdoc/html/render/print_item.rs
+++ b/src/librustdoc/html/render/print_item.rs
@@ -470,10 +470,11 @@ fn extra_info_tags(item: &clean::Item, parent: &clean::Item, tcx: TyCtxt<'_>) ->
 
     // The trailing space after each tag is to space it properly against the rest of the docs.
     if let Some(depr) = &item.deprecation(tcx) {
-        let mut message = "Deprecated";
-        if !stability::deprecation_in_effect(depr) {
-            message = "Deprecation planned";
-        }
+        let message = if stability::deprecation_in_effect(depr) {
+            "Deprecated"
+        } else {
+            "Deprecation planned"
+        };
         tags += &tag_html("deprecated", "", message);
     }
 
diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs
index e22ac6ec19b..146221f5806 100644
--- a/src/librustdoc/html/render/search_index.rs
+++ b/src/librustdoc/html/render/search_index.rs
@@ -40,6 +40,7 @@ pub(crate) fn build_index<'tcx>(
                 parent_idx: None,
                 search_type: get_function_type_for_search(item, tcx, impl_generics.as_ref(), cache),
                 aliases: item.attrs.get_doc_aliases(),
+                deprecation: item.deprecation(tcx),
             });
         }
     }
@@ -251,7 +252,17 @@ pub(crate) fn build_index<'tcx>(
             )?;
             crate_data.serialize_field(
                 "q",
-                &self.items.iter().map(|item| &item.path).collect::<Vec<_>>(),
+                &self
+                    .items
+                    .iter()
+                    .enumerate()
+                    // Serialize as an array of item indices and full paths
+                    .filter_map(
+                        |(index, item)| {
+                            if item.path.is_empty() { None } else { Some((index, &item.path)) }
+                        },
+                    )
+                    .collect::<Vec<_>>(),
             )?;
             crate_data.serialize_field(
                 "d",
@@ -305,6 +316,16 @@ pub(crate) fn build_index<'tcx>(
                     .collect::<Vec<_>>(),
             )?;
             crate_data.serialize_field(
+                "c",
+                &self
+                    .items
+                    .iter()
+                    .enumerate()
+                    // Serialize as an array of deprecated item indices
+                    .filter_map(|(index, item)| item.deprecation.map(|_| index))
+                    .collect::<Vec<_>>(),
+            )?;
+            crate_data.serialize_field(
                 "p",
                 &self.paths.iter().map(|(it, s)| (it, s.as_str())).collect::<Vec<_>>(),
             )?;
diff --git a/src/librustdoc/html/render/sidebar.rs b/src/librustdoc/html/render/sidebar.rs
new file mode 100644
index 00000000000..94ad4753d7c
--- /dev/null
+++ b/src/librustdoc/html/render/sidebar.rs
@@ -0,0 +1,561 @@
+use std::{borrow::Cow, rc::Rc};
+
+use askama::Template;
+use rustc_data_structures::fx::FxHashSet;
+use rustc_hir::{def::CtorKind, def_id::DefIdSet};
+use rustc_middle::ty::{self, TyCtxt};
+
+use crate::{
+    clean,
+    formats::{item_type::ItemType, Impl},
+    html::{format::Buffer, markdown::IdMap},
+};
+
+use super::{item_ty_to_section, Context, ItemSection};
+
+#[derive(Template)]
+#[template(path = "sidebar.html")]
+pub(super) struct Sidebar<'a> {
+    pub(super) title_prefix: &'static str,
+    pub(super) title: &'a str,
+    pub(super) is_crate: bool,
+    pub(super) version: &'a str,
+    pub(super) blocks: Vec<LinkBlock<'a>>,
+    pub(super) path: String,
+}
+
+impl<'a> Sidebar<'a> {
+    /// Only create a `<section>` if there are any blocks
+    /// which should actually be rendered.
+    pub fn should_render_blocks(&self) -> bool {
+        self.blocks.iter().any(LinkBlock::should_render)
+    }
+}
+
+/// A sidebar section such as 'Methods'.
+pub(crate) struct LinkBlock<'a> {
+    /// The name of this section, e.g. 'Methods'
+    /// as well as the link to it, e.g. `#implementations`.
+    /// Will be rendered inside an `<h3>` tag
+    heading: Link<'a>,
+    links: Vec<Link<'a>>,
+    /// Render the heading even if there are no links
+    force_render: bool,
+}
+
+impl<'a> LinkBlock<'a> {
+    pub fn new(heading: Link<'a>, links: Vec<Link<'a>>) -> Self {
+        Self { heading, links, force_render: false }
+    }
+
+    pub fn forced(heading: Link<'a>) -> Self {
+        Self { heading, links: vec![], force_render: true }
+    }
+
+    pub fn should_render(&self) -> bool {
+        self.force_render || !self.links.is_empty()
+    }
+}
+
+/// A link to an item. Content should not be escaped.
+#[derive(PartialOrd, Ord, PartialEq, Eq, Hash, Clone)]
+pub(crate) struct Link<'a> {
+    /// The content for the anchor tag
+    name: Cow<'a, str>,
+    /// The id of an anchor within the page (without a `#` prefix)
+    href: Cow<'a, str>,
+}
+
+impl<'a> Link<'a> {
+    pub fn new(href: impl Into<Cow<'a, str>>, name: impl Into<Cow<'a, str>>) -> Self {
+        Self { href: href.into(), name: name.into() }
+    }
+    pub fn empty() -> Link<'static> {
+        Link::new("", "")
+    }
+}
+
+pub(super) fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buffer) {
+    let blocks: Vec<LinkBlock<'_>> = match *it.kind {
+        clean::StructItem(ref s) => sidebar_struct(cx, it, s),
+        clean::TraitItem(ref t) => sidebar_trait(cx, it, t),
+        clean::PrimitiveItem(_) => sidebar_primitive(cx, it),
+        clean::UnionItem(ref u) => sidebar_union(cx, it, u),
+        clean::EnumItem(ref e) => sidebar_enum(cx, it, e),
+        clean::TypedefItem(_) => sidebar_typedef(cx, it),
+        clean::ModuleItem(ref m) => vec![sidebar_module(&m.items)],
+        clean::ForeignTypeItem => sidebar_foreign_type(cx, it),
+        _ => vec![],
+    };
+    // The sidebar is designed to display sibling functions, modules and
+    // other miscellaneous information. since there are lots of sibling
+    // items (and that causes quadratic growth in large modules),
+    // we refactor common parts into a shared JavaScript file per module.
+    // still, we don't move everything into JS because we want to preserve
+    // as much HTML as possible in order to allow non-JS-enabled browsers
+    // to navigate the documentation (though slightly inefficiently).
+    let (title_prefix, title) = if it.is_struct()
+        || it.is_trait()
+        || it.is_primitive()
+        || it.is_union()
+        || it.is_enum()
+        || it.is_mod()
+        || it.is_typedef()
+    {
+        (
+            match *it.kind {
+                clean::ModuleItem(..) if it.is_crate() => "Crate ",
+                clean::ModuleItem(..) => "Module ",
+                _ => "",
+            },
+            it.name.as_ref().unwrap().as_str(),
+        )
+    } else {
+        ("", "")
+    };
+    let version = if it.is_crate() {
+        cx.cache().crate_version.as_ref().map(String::as_str).unwrap_or_default()
+    } else {
+        ""
+    };
+    let path: String = if !it.is_mod() {
+        cx.current.iter().map(|s| s.as_str()).intersperse("::").collect()
+    } else {
+        "".into()
+    };
+    let sidebar = Sidebar { title_prefix, title, is_crate: it.is_crate(), version, blocks, path };
+    sidebar.render_into(buffer).unwrap();
+}
+
+fn get_struct_fields_name<'a>(fields: &'a [clean::Item]) -> Vec<Link<'a>> {
+    let mut fields = fields
+        .iter()
+        .filter(|f| matches!(*f.kind, clean::StructFieldItem(..)))
+        .filter_map(|f| {
+            f.name.as_ref().map(|name| Link::new(format!("structfield.{name}"), name.as_str()))
+        })
+        .collect::<Vec<Link<'a>>>();
+    fields.sort();
+    fields
+}
+
+fn sidebar_struct<'a>(
+    cx: &'a Context<'_>,
+    it: &'a clean::Item,
+    s: &'a clean::Struct,
+) -> Vec<LinkBlock<'a>> {
+    let fields = get_struct_fields_name(&s.fields);
+    let field_name = match s.ctor_kind {
+        Some(CtorKind::Fn) => Some("Tuple Fields"),
+        None => Some("Fields"),
+        _ => None,
+    };
+    let mut items = vec![];
+    if let Some(name) = field_name {
+        items.push(LinkBlock::new(Link::new("fields", name), fields));
+    }
+    sidebar_assoc_items(cx, it, &mut items);
+    items
+}
+
+fn sidebar_trait<'a>(
+    cx: &'a Context<'_>,
+    it: &'a clean::Item,
+    t: &'a clean::Trait,
+) -> Vec<LinkBlock<'a>> {
+    fn filter_items<'a>(
+        items: &'a [clean::Item],
+        filt: impl Fn(&clean::Item) -> bool,
+        ty: &str,
+    ) -> Vec<Link<'a>> {
+        let mut res = items
+            .iter()
+            .filter_map(|m: &clean::Item| match m.name {
+                Some(ref name) if filt(m) => Some(Link::new(format!("{ty}.{name}"), name.as_str())),
+                _ => None,
+            })
+            .collect::<Vec<Link<'a>>>();
+        res.sort();
+        res
+    }
+
+    let req_assoc = filter_items(&t.items, |m| m.is_ty_associated_type(), "associatedtype");
+    let prov_assoc = filter_items(&t.items, |m| m.is_associated_type(), "associatedtype");
+    let req_assoc_const =
+        filter_items(&t.items, |m| m.is_ty_associated_const(), "associatedconstant");
+    let prov_assoc_const =
+        filter_items(&t.items, |m| m.is_associated_const(), "associatedconstant");
+    let req_method = filter_items(&t.items, |m| m.is_ty_method(), "tymethod");
+    let prov_method = filter_items(&t.items, |m| m.is_method(), "method");
+    let mut foreign_impls = vec![];
+    if let Some(implementors) = cx.cache().implementors.get(&it.item_id.expect_def_id()) {
+        foreign_impls.extend(
+            implementors
+                .iter()
+                .filter(|i| !i.is_on_local_type(cx))
+                .filter_map(|i| super::extract_for_impl_name(&i.impl_item, cx))
+                .map(|(name, id)| Link::new(id, name)),
+        );
+        foreign_impls.sort();
+    }
+
+    let mut blocks: Vec<LinkBlock<'_>> = [
+        ("required-associated-types", "Required Associated Types", req_assoc),
+        ("provided-associated-types", "Provided Associated Types", prov_assoc),
+        ("required-associated-consts", "Required Associated Constants", req_assoc_const),
+        ("provided-associated-consts", "Provided Associated Constants", prov_assoc_const),
+        ("required-methods", "Required Methods", req_method),
+        ("provided-methods", "Provided Methods", prov_method),
+        ("foreign-impls", "Implementations on Foreign Types", foreign_impls),
+    ]
+    .into_iter()
+    .map(|(id, title, items)| LinkBlock::new(Link::new(id, title), items))
+    .collect();
+    sidebar_assoc_items(cx, it, &mut blocks);
+    blocks.push(LinkBlock::forced(Link::new("implementors", "Implementors")));
+    if t.is_auto(cx.tcx()) {
+        blocks.push(LinkBlock::forced(Link::new("synthetic-implementors", "Auto Implementors")));
+    }
+    blocks
+}
+
+fn sidebar_primitive<'a>(cx: &'a Context<'_>, it: &'a clean::Item) -> Vec<LinkBlock<'a>> {
+    if it.name.map(|n| n.as_str() != "reference").unwrap_or(false) {
+        let mut items = vec![];
+        sidebar_assoc_items(cx, it, &mut items);
+        items
+    } else {
+        let shared = Rc::clone(&cx.shared);
+        let (concrete, synthetic, blanket_impl) =
+            super::get_filtered_impls_for_reference(&shared, it);
+
+        sidebar_render_assoc_items(cx, &mut IdMap::new(), concrete, synthetic, blanket_impl).into()
+    }
+}
+
+fn sidebar_typedef<'a>(cx: &'a Context<'_>, it: &'a clean::Item) -> Vec<LinkBlock<'a>> {
+    let mut items = vec![];
+    sidebar_assoc_items(cx, it, &mut items);
+    items
+}
+
+fn sidebar_union<'a>(
+    cx: &'a Context<'_>,
+    it: &'a clean::Item,
+    u: &'a clean::Union,
+) -> Vec<LinkBlock<'a>> {
+    let fields = get_struct_fields_name(&u.fields);
+    let mut items = vec![LinkBlock::new(Link::new("fields", "Fields"), fields)];
+    sidebar_assoc_items(cx, it, &mut items);
+    items
+}
+
+/// Adds trait implementations into the blocks of links
+fn sidebar_assoc_items<'a>(
+    cx: &'a Context<'_>,
+    it: &'a clean::Item,
+    links: &mut Vec<LinkBlock<'a>>,
+) {
+    let did = it.item_id.expect_def_id();
+    let cache = cx.cache();
+
+    let mut assoc_consts = Vec::new();
+    let mut methods = Vec::new();
+    if let Some(v) = cache.impls.get(&did) {
+        let mut used_links = FxHashSet::default();
+        let mut id_map = IdMap::new();
+
+        {
+            let used_links_bor = &mut used_links;
+            assoc_consts.extend(
+                v.iter()
+                    .filter(|i| i.inner_impl().trait_.is_none())
+                    .flat_map(|i| get_associated_constants(i.inner_impl(), used_links_bor)),
+            );
+            // We want links' order to be reproducible so we don't use unstable sort.
+            assoc_consts.sort();
+
+            #[rustfmt::skip] // rustfmt makes the pipeline less readable
+            methods.extend(
+                v.iter()
+                    .filter(|i| i.inner_impl().trait_.is_none())
+                    .flat_map(|i| get_methods(i.inner_impl(), false, used_links_bor, false, cx.tcx())),
+            );
+
+            // We want links' order to be reproducible so we don't use unstable sort.
+            methods.sort();
+        }
+
+        let mut deref_methods = Vec::new();
+        let [concrete, synthetic, blanket] = if v.iter().any(|i| i.inner_impl().trait_.is_some()) {
+            if let Some(impl_) =
+                v.iter().find(|i| i.trait_did() == cx.tcx().lang_items().deref_trait())
+            {
+                let mut derefs = DefIdSet::default();
+                derefs.insert(did);
+                sidebar_deref_methods(
+                    cx,
+                    &mut deref_methods,
+                    impl_,
+                    v,
+                    &mut derefs,
+                    &mut used_links,
+                );
+            }
+
+            let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) =
+                v.iter().partition::<Vec<_>, _>(|i| i.inner_impl().kind.is_auto());
+            let (blanket_impl, concrete): (Vec<&Impl>, Vec<&Impl>) =
+                concrete.into_iter().partition::<Vec<_>, _>(|i| i.inner_impl().kind.is_blanket());
+
+            sidebar_render_assoc_items(cx, &mut id_map, concrete, synthetic, blanket_impl)
+        } else {
+            std::array::from_fn(|_| LinkBlock::new(Link::empty(), vec![]))
+        };
+
+        let mut blocks = vec![
+            LinkBlock::new(Link::new("implementations", "Associated Constants"), assoc_consts),
+            LinkBlock::new(Link::new("implementations", "Methods"), methods),
+        ];
+        blocks.append(&mut deref_methods);
+        blocks.extend([concrete, synthetic, blanket]);
+        links.append(&mut blocks);
+    }
+}
+
+fn sidebar_deref_methods<'a>(
+    cx: &'a Context<'_>,
+    out: &mut Vec<LinkBlock<'a>>,
+    impl_: &Impl,
+    v: &[Impl],
+    derefs: &mut DefIdSet,
+    used_links: &mut FxHashSet<String>,
+) {
+    let c = cx.cache();
+
+    debug!("found Deref: {:?}", impl_);
+    if let Some((target, real_target)) =
+        impl_.inner_impl().items.iter().find_map(|item| match *item.kind {
+            clean::AssocTypeItem(box ref t, _) => Some(match *t {
+                clean::Typedef { item_type: Some(ref type_), .. } => (type_, &t.type_),
+                _ => (&t.type_, &t.type_),
+            }),
+            _ => None,
+        })
+    {
+        debug!("found target, real_target: {:?} {:?}", target, real_target);
+        if let Some(did) = target.def_id(c) &&
+            let Some(type_did) = impl_.inner_impl().for_.def_id(c) &&
+            // `impl Deref<Target = S> for S`
+            (did == type_did || !derefs.insert(did))
+        {
+            // Avoid infinite cycles
+            return;
+        }
+        let deref_mut = v.iter().any(|i| i.trait_did() == cx.tcx().lang_items().deref_mut_trait());
+        let inner_impl = target
+            .def_id(c)
+            .or_else(|| {
+                target.primitive_type().and_then(|prim| c.primitive_locations.get(&prim).cloned())
+            })
+            .and_then(|did| c.impls.get(&did));
+        if let Some(impls) = inner_impl {
+            debug!("found inner_impl: {:?}", impls);
+            let mut ret = impls
+                .iter()
+                .filter(|i| i.inner_impl().trait_.is_none())
+                .flat_map(|i| get_methods(i.inner_impl(), true, used_links, deref_mut, cx.tcx()))
+                .collect::<Vec<_>>();
+            if !ret.is_empty() {
+                let id = if let Some(target_def_id) = real_target.def_id(c) {
+                    Cow::Borrowed(
+                        cx.deref_id_map
+                            .get(&target_def_id)
+                            .expect("Deref section without derived id")
+                            .as_str(),
+                    )
+                } else {
+                    Cow::Borrowed("deref-methods")
+                };
+                let title = format!(
+                    "Methods from {:#}<Target={:#}>",
+                    impl_.inner_impl().trait_.as_ref().unwrap().print(cx),
+                    real_target.print(cx),
+                );
+                // We want links' order to be reproducible so we don't use unstable sort.
+                ret.sort();
+                out.push(LinkBlock::new(Link::new(id, title), ret));
+            }
+        }
+
+        // Recurse into any further impls that might exist for `target`
+        if let Some(target_did) = target.def_id(c) &&
+            let Some(target_impls) = c.impls.get(&target_did) &&
+            let Some(target_deref_impl) = target_impls.iter().find(|i| {
+                i.inner_impl()
+                    .trait_
+                    .as_ref()
+                    .map(|t| Some(t.def_id()) == cx.tcx().lang_items().deref_trait())
+                    .unwrap_or(false)
+            })
+        {
+            sidebar_deref_methods(
+                cx,
+                out,
+                target_deref_impl,
+                target_impls,
+                derefs,
+                used_links,
+            );
+        }
+    }
+}
+
+fn sidebar_enum<'a>(
+    cx: &'a Context<'_>,
+    it: &'a clean::Item,
+    e: &'a clean::Enum,
+) -> Vec<LinkBlock<'a>> {
+    let mut variants = e
+        .variants()
+        .filter_map(|v| v.name)
+        .map(|name| Link::new(format!("variant.{name}"), name.to_string()))
+        .collect::<Vec<_>>();
+    variants.sort_unstable();
+
+    let mut items = vec![LinkBlock::new(Link::new("variants", "Variants"), variants)];
+    sidebar_assoc_items(cx, it, &mut items);
+    items
+}
+
+pub(crate) fn sidebar_module_like(
+    item_sections_in_use: FxHashSet<ItemSection>,
+) -> LinkBlock<'static> {
+    let item_sections = ItemSection::ALL
+        .iter()
+        .copied()
+        .filter(|sec| item_sections_in_use.contains(sec))
+        .map(|sec| Link::new(sec.id(), sec.name()))
+        .collect();
+    LinkBlock::new(Link::empty(), item_sections)
+}
+
+fn sidebar_module(items: &[clean::Item]) -> LinkBlock<'static> {
+    let item_sections_in_use: FxHashSet<_> = items
+        .iter()
+        .filter(|it| {
+            !it.is_stripped()
+                && it
+                    .name
+                    .or_else(|| {
+                        if let clean::ImportItem(ref i) = *it.kind &&
+                            let clean::ImportKind::Simple(s) = i.kind { Some(s) } else { None }
+                    })
+                    .is_some()
+        })
+        .map(|it| item_ty_to_section(it.type_()))
+        .collect();
+
+    sidebar_module_like(item_sections_in_use)
+}
+
+fn sidebar_foreign_type<'a>(cx: &'a Context<'_>, it: &'a clean::Item) -> Vec<LinkBlock<'a>> {
+    let mut items = vec![];
+    sidebar_assoc_items(cx, it, &mut items);
+    items
+}
+
+/// Renders the trait implementations for this type
+fn sidebar_render_assoc_items(
+    cx: &Context<'_>,
+    id_map: &mut IdMap,
+    concrete: Vec<&Impl>,
+    synthetic: Vec<&Impl>,
+    blanket_impl: Vec<&Impl>,
+) -> [LinkBlock<'static>; 3] {
+    let format_impls = |impls: Vec<&Impl>, id_map: &mut IdMap| {
+        let mut links = FxHashSet::default();
+
+        let mut ret = impls
+            .iter()
+            .filter_map(|it| {
+                let trait_ = it.inner_impl().trait_.as_ref()?;
+                let encoded =
+                    id_map.derive(super::get_id_for_impl(&it.inner_impl().for_, Some(trait_), cx));
+
+                let prefix = match it.inner_impl().polarity {
+                    ty::ImplPolarity::Positive | ty::ImplPolarity::Reservation => "",
+                    ty::ImplPolarity::Negative => "!",
+                };
+                let generated = Link::new(encoded, format!("{prefix}{:#}", trait_.print(cx)));
+                if links.insert(generated.clone()) { Some(generated) } else { None }
+            })
+            .collect::<Vec<Link<'static>>>();
+        ret.sort();
+        ret
+    };
+
+    let concrete = format_impls(concrete, id_map);
+    let synthetic = format_impls(synthetic, id_map);
+    let blanket = format_impls(blanket_impl, id_map);
+    [
+        LinkBlock::new(Link::new("trait-implementations", "Trait Implementations"), concrete),
+        LinkBlock::new(
+            Link::new("synthetic-implementations", "Auto Trait Implementations"),
+            synthetic,
+        ),
+        LinkBlock::new(Link::new("blanket-implementations", "Blanket Implementations"), blanket),
+    ]
+}
+
+fn get_next_url(used_links: &mut FxHashSet<String>, url: String) -> String {
+    if used_links.insert(url.clone()) {
+        return url;
+    }
+    let mut add = 1;
+    while !used_links.insert(format!("{}-{}", url, add)) {
+        add += 1;
+    }
+    format!("{}-{}", url, add)
+}
+
+fn get_methods<'a>(
+    i: &'a clean::Impl,
+    for_deref: bool,
+    used_links: &mut FxHashSet<String>,
+    deref_mut: bool,
+    tcx: TyCtxt<'_>,
+) -> Vec<Link<'a>> {
+    i.items
+        .iter()
+        .filter_map(|item| match item.name {
+            Some(ref name) if !name.is_empty() && item.is_method() => {
+                if !for_deref || super::should_render_item(item, deref_mut, tcx) {
+                    Some(Link::new(
+                        get_next_url(used_links, format!("{}.{}", ItemType::Method, name)),
+                        name.as_str(),
+                    ))
+                } else {
+                    None
+                }
+            }
+            _ => None,
+        })
+        .collect::<Vec<_>>()
+}
+
+fn get_associated_constants<'a>(
+    i: &'a clean::Impl,
+    used_links: &mut FxHashSet<String>,
+) -> Vec<Link<'a>> {
+    i.items
+        .iter()
+        .filter_map(|item| match item.name {
+            Some(ref name) if !name.is_empty() && item.is_associated_const() => Some(Link::new(
+                get_next_url(used_links, format!("{}.{}", ItemType::AssocConst, name)),
+                name.as_str(),
+            )),
+            _ => None,
+        })
+        .collect::<Vec<_>>()
+}
diff --git a/src/librustdoc/html/static/COPYRIGHT.txt b/src/librustdoc/html/static/COPYRIGHT.txt
index 34e48134cc3..1447df792f6 100644
--- a/src/librustdoc/html/static/COPYRIGHT.txt
+++ b/src/librustdoc/html/static/COPYRIGHT.txt
@@ -1,3 +1,5 @@
+# REUSE-IgnoreStart
+
 These documentation pages include resources by third parties. This copyright
 file applies only to those resources. The following third party resources are
 included, and carry their own copyright notices and license terms:
@@ -44,3 +46,5 @@ included, and carry their own copyright notices and license terms:
     See SourceSerif4-LICENSE.md.
 
 This copyright file is intended to be distributed with rustdoc output.
+
+# REUSE-IgnoreEnd
diff --git a/src/librustdoc/html/static/fonts/FiraSans-LICENSE.txt b/src/librustdoc/html/static/fonts/FiraSans-LICENSE.txt
index ff9afab064a..d7e9c149b7e 100644
--- a/src/librustdoc/html/static/fonts/FiraSans-LICENSE.txt
+++ b/src/librustdoc/html/static/fonts/FiraSans-LICENSE.txt
@@ -1,3 +1,5 @@
+// REUSE-IgnoreStart
+
 Digitized data copyright (c) 2012-2015, The Mozilla Foundation and Telefonica S.A.
 with Reserved Font Name < Fira >,
 
@@ -92,3 +94,5 @@ INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
 DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
 OTHER DEALINGS IN THE FONT SOFTWARE.
+
+// REUSE-IgnoreEnd
diff --git a/src/librustdoc/html/static/fonts/NanumBarunGothic-LICENSE.txt b/src/librustdoc/html/static/fonts/NanumBarunGothic-LICENSE.txt
index 0bf46682b5b..4b3edc29eb9 100644
--- a/src/librustdoc/html/static/fonts/NanumBarunGothic-LICENSE.txt
+++ b/src/librustdoc/html/static/fonts/NanumBarunGothic-LICENSE.txt
@@ -1,3 +1,5 @@
+// REUSE-IgnoreStart
+
 Copyright (c) 2010, NAVER Corporation (https://www.navercorp.com/),
 
 with Reserved Font Name Nanum, Naver Nanum, NanumGothic, Naver NanumGothic,
@@ -97,3 +99,5 @@ INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
 DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
 OTHER DEALINGS IN THE FONT SOFTWARE.
+
+// REUSE-IgnoreEnd
diff --git a/src/librustdoc/html/static/fonts/SourceCodePro-LICENSE.txt b/src/librustdoc/html/static/fonts/SourceCodePro-LICENSE.txt
index 07542572e33..0d2941e148d 100644
--- a/src/librustdoc/html/static/fonts/SourceCodePro-LICENSE.txt
+++ b/src/librustdoc/html/static/fonts/SourceCodePro-LICENSE.txt
@@ -1,3 +1,5 @@
+// REUSE-IgnoreStart
+
 Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries.
 
 This Font Software is licensed under the SIL Open Font License, Version 1.1.
@@ -91,3 +93,5 @@ INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
 DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
 OTHER DEALINGS IN THE FONT SOFTWARE.
+
+// REUSE-IgnoreEnd
diff --git a/src/librustdoc/html/static/fonts/SourceSerif4-LICENSE.md b/src/librustdoc/html/static/fonts/SourceSerif4-LICENSE.md
index 5871e1f3d1b..175fa4f47ae 100644
--- a/src/librustdoc/html/static/fonts/SourceSerif4-LICENSE.md
+++ b/src/librustdoc/html/static/fonts/SourceSerif4-LICENSE.md
@@ -1,3 +1,6 @@
+<!-- REUSE-IgnoreStart -->
+
+Copyright 2014-2021 Adobe (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe in the United States and/or other countries.
 Copyright 2014 - 2023 Adobe (http://www.adobe.com/), with Reserved Font Name ‘Source’. All Rights Reserved. Source is a trademark of Adobe in the United States and/or other countries.
 
 This Font Software is licensed under the SIL Open Font License, Version 1.1.
@@ -91,3 +94,5 @@ INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
 DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
 OTHER DEALINGS IN THE FONT SOFTWARE.
+
+<!-- REUSE-IgnoreEnd -->
diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js
index b98bced4126..c71ce2c3001 100644
--- a/src/librustdoc/html/static/js/search.js
+++ b/src/librustdoc/html/static/js/search.js
@@ -76,39 +76,111 @@ function printTab(nb) {
 }
 
 /**
- * A function to compute the Levenshtein distance between two strings
- * Licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported
- * Full License can be found at http://creativecommons.org/licenses/by-sa/3.0/legalcode
- * This code is an unmodified version of the code written by Marco de Wit
- * and was found at https://stackoverflow.com/a/18514751/745719
+ * The [edit distance] is a metric for measuring the difference between two strings.
+ *
+ * [edit distance]: https://en.wikipedia.org/wiki/Edit_distance
  */
-const levenshtein_row2 = [];
-function levenshtein(s1, s2) {
-    if (s1 === s2) {
-        return 0;
-    }
-    const s1_len = s1.length, s2_len = s2.length;
-    if (s1_len && s2_len) {
-        let i1 = 0, i2 = 0, a, b, c, c2;
-        const row = levenshtein_row2;
-        while (i1 < s1_len) {
-            row[i1] = ++i1;
-        }
-        while (i2 < s2_len) {
-            c2 = s2.charCodeAt(i2);
-            a = i2;
-            ++i2;
-            b = i2;
-            for (i1 = 0; i1 < s1_len; ++i1) {
-                c = a + (s1.charCodeAt(i1) !== c2 ? 1 : 0);
-                a = row[i1];
-                b = b < a ? (b < c ? b + 1 : c) : (a < c ? a + 1 : c);
-                row[i1] = b;
-            }
-        }
-        return b;
-    }
-    return s1_len + s2_len;
+
+/*
+ * This function was translated, mostly line-for-line, from
+ * https://github.com/rust-lang/rust/blob/ff4b772f805ec1e/compiler/rustc_span/src/edit_distance.rs
+ *
+ * The current implementation is the restricted Damerau-Levenshtein algorithm. It is restricted
+ * because it does not permit modifying characters that have already been transposed. The specific
+ * algorithm should not matter to the caller of the methods, which is why it is not noted in the
+ * documentation.
+ */
+const editDistanceState = {
+    current: [],
+    prev: [],
+    prevPrev: [],
+    calculate: function calculate(a, b, limit) {
+        // Ensure that `b` is the shorter string, minimizing memory use.
+        if (a.length < b.length) {
+            const aTmp = a;
+            a = b;
+            b = aTmp;
+        }
+
+        const minDist = a.length - b.length;
+        // If we know the limit will be exceeded, we can return early.
+        if (minDist > limit) {
+            return limit + 1;
+        }
+
+        // Strip common prefix.
+        // We know that `b` is the shorter string, so we don't need to check
+        // `a.length`.
+        while (b.length > 0 && b[0] === a[0]) {
+            a = a.substring(1);
+            b = b.substring(1);
+        }
+        // Strip common suffix.
+        while (b.length > 0 && b[b.length - 1] === a[a.length - 1]) {
+            a = a.substring(0, a.length - 1);
+            b = b.substring(0, b.length - 1);
+        }
+
+        // If either string is empty, the distance is the length of the other.
+        // We know that `b` is the shorter string, so we don't need to check `a`.
+        if (b.length === 0) {
+            return minDist;
+        }
+
+        const aLength = a.length;
+        const bLength = b.length;
+
+        for (let i = 0; i <= bLength; ++i) {
+            this.current[i] = 0;
+            this.prev[i] = i;
+            this.prevPrev[i] = Number.MAX_VALUE;
+        }
+
+        // row by row
+        for (let i = 1; i <= aLength; ++i) {
+            this.current[0] = i;
+            const aIdx = i - 1;
+
+            // column by column
+            for (let j = 1; j <= bLength; ++j) {
+                const bIdx = j - 1;
+
+                // There is no cost to substitute a character with itself.
+                const substitutionCost = a[aIdx] === b[bIdx] ? 0 : 1;
+
+                this.current[j] = Math.min(
+                    // deletion
+                    this.prev[j] + 1,
+                    // insertion
+                    this.current[j - 1] + 1,
+                    // substitution
+                    this.prev[j - 1] + substitutionCost
+                );
+
+                if ((i > 1) && (j > 1) && (a[aIdx] === b[bIdx - 1]) && (a[aIdx - 1] === b[bIdx])) {
+                    // transposition
+                    this.current[j] = Math.min(
+                        this.current[j],
+                        this.prevPrev[j - 2] + 1
+                    );
+                }
+            }
+
+            // Rotate the buffers, reusing the memory
+            const prevPrevTmp = this.prevPrev;
+            this.prevPrev = this.prev;
+            this.prev = this.current;
+            this.current = prevPrevTmp;
+        }
+
+        // `prev` because we already rotated the buffers.
+        const distance = this.prev[bLength];
+        return distance <= limit ? distance : (limit + 1);
+    },
+};
+
+function editDistance(a, b, limit) {
+    return editDistanceState.calculate(a, b, limit);
 }
 
 function initSearch(rawSearchIndex) {
@@ -802,7 +874,7 @@ function initSearch(rawSearchIndex) {
             for (const result of results) {
                 if (result.id > -1) {
                     const obj = searchIndex[result.id];
-                    obj.lev = result.lev;
+                    obj.dist = result.dist;
                     const res = buildHrefAndPath(obj);
                     obj.displayPath = pathSplitter(res[0]);
                     obj.fullPath = obj.displayPath + obj.name;
@@ -860,8 +932,8 @@ function initSearch(rawSearchIndex) {
 
                 // Sort by distance in the path part, if specified
                 // (less changes required to match means higher rankings)
-                a = aaa.path_lev;
-                b = bbb.path_lev;
+                a = aaa.path_dist;
+                b = bbb.path_dist;
                 if (a !== b) {
                     return a - b;
                 }
@@ -875,8 +947,15 @@ function initSearch(rawSearchIndex) {
 
                 // Sort by distance in the name part, the last part of the path
                 // (less changes required to match means higher rankings)
-                a = (aaa.lev);
-                b = (bbb.lev);
+                a = (aaa.dist);
+                b = (bbb.dist);
+                if (a !== b) {
+                    return a - b;
+                }
+
+                // sort deprecated items later
+                a = aaa.item.deprecated;
+                b = bbb.item.deprecated;
                 if (a !== b) {
                     return a - b;
                 }
@@ -961,19 +1040,20 @@ function initSearch(rawSearchIndex) {
 
         /**
          * This function checks if the object (`row`) generics match the given type (`elem`)
-         * generics. If there are no generics on `row`, `defaultLev` is returned.
+         * generics. If there are no generics on `row`, `defaultDistance` is returned.
          *
-         * @param {Row} row            - The object to check.
-         * @param {QueryElement} elem  - The element from the parsed query.
-         * @param {integer} defaultLev - This is the value to return in case there are no generics.
+         * @param {Row} row                 - The object to check.
+         * @param {QueryElement} elem       - The element from the parsed query.
+         * @param {integer} defaultDistance - This is the value to return in case there are no
+         *                                    generics.
          *
-         * @return {integer}           - Returns the best match (if any) or `maxLevDistance + 1`.
+         * @return {integer}           - Returns the best match (if any) or `maxEditDistance + 1`.
          */
-        function checkGenerics(row, elem, defaultLev, maxLevDistance) {
+        function checkGenerics(row, elem, defaultDistance, maxEditDistance) {
             if (row.generics.length === 0) {
-                return elem.generics.length === 0 ? defaultLev : maxLevDistance + 1;
+                return elem.generics.length === 0 ? defaultDistance : maxEditDistance + 1;
             } else if (row.generics.length > 0 && row.generics[0].name === null) {
-                return checkGenerics(row.generics[0], elem, defaultLev, maxLevDistance);
+                return checkGenerics(row.generics[0], elem, defaultDistance, maxEditDistance);
             }
             // The names match, but we need to be sure that all generics kinda
             // match as well.
@@ -984,8 +1064,9 @@ function initSearch(rawSearchIndex) {
                     elem_name = entry.name;
                     if (elem_name === "") {
                         // Pure generic, needs to check into it.
-                        if (checkGenerics(entry, elem, maxLevDistance + 1, maxLevDistance) !== 0) {
-                            return maxLevDistance + 1;
+                        if (checkGenerics(entry, elem, maxEditDistance + 1, maxEditDistance)
+                            !== 0) {
+                            return maxEditDistance + 1;
                         }
                         continue;
                     }
@@ -1012,7 +1093,7 @@ function initSearch(rawSearchIndex) {
                         }
                     }
                     if (match === null) {
-                        return maxLevDistance + 1;
+                        return maxEditDistance + 1;
                     }
                     elems[match] -= 1;
                     if (elems[match] === 0) {
@@ -1021,7 +1102,7 @@ function initSearch(rawSearchIndex) {
                 }
                 return 0;
             }
-            return maxLevDistance + 1;
+            return maxEditDistance + 1;
         }
 
         /**
@@ -1031,17 +1112,17 @@ function initSearch(rawSearchIndex) {
           * @param {Row} row
           * @param {QueryElement} elem    - The element from the parsed query.
           *
-          * @return {integer} - Returns a Levenshtein distance to the best match.
+          * @return {integer} - Returns an edit distance to the best match.
           */
-        function checkIfInGenerics(row, elem, maxLevDistance) {
-            let lev = maxLevDistance + 1;
+        function checkIfInGenerics(row, elem, maxEditDistance) {
+            let dist = maxEditDistance + 1;
             for (const entry of row.generics) {
-                lev = Math.min(checkType(entry, elem, true, maxLevDistance), lev);
-                if (lev === 0) {
+                dist = Math.min(checkType(entry, elem, true, maxEditDistance), dist);
+                if (dist === 0) {
                     break;
                 }
             }
-            return lev;
+            return dist;
         }
 
         /**
@@ -1052,21 +1133,21 @@ function initSearch(rawSearchIndex) {
           * @param {QueryElement} elem      - The element from the parsed query.
           * @param {boolean} literalSearch
           *
-          * @return {integer} - Returns a Levenshtein distance to the best match. If there is
-          *                     no match, returns `maxLevDistance + 1`.
+          * @return {integer} - Returns an edit distance to the best match. If there is
+          *                     no match, returns `maxEditDistance + 1`.
           */
-        function checkType(row, elem, literalSearch, maxLevDistance) {
+        function checkType(row, elem, literalSearch, maxEditDistance) {
             if (row.name === null) {
                 // This is a pure "generic" search, no need to run other checks.
                 if (row.generics.length > 0) {
-                    return checkIfInGenerics(row, elem, maxLevDistance);
+                    return checkIfInGenerics(row, elem, maxEditDistance);
                 }
-                return maxLevDistance + 1;
+                return maxEditDistance + 1;
             }
 
-            let lev = levenshtein(row.name, elem.name);
+            let dist = editDistance(row.name, elem.name, maxEditDistance);
             if (literalSearch) {
-                if (lev !== 0) {
+                if (dist !== 0) {
                     // The name didn't match, let's try to check if the generics do.
                     if (elem.generics.length === 0) {
                         const checkGeneric = row.generics.length > 0;
@@ -1075,44 +1156,44 @@ function initSearch(rawSearchIndex) {
                             return 0;
                         }
                     }
-                    return maxLevDistance + 1;
+                    return maxEditDistance + 1;
                 } else if (elem.generics.length > 0) {
-                    return checkGenerics(row, elem, maxLevDistance + 1, maxLevDistance);
+                    return checkGenerics(row, elem, maxEditDistance + 1, maxEditDistance);
                 }
                 return 0;
             } else if (row.generics.length > 0) {
                 if (elem.generics.length === 0) {
-                    if (lev === 0) {
+                    if (dist === 0) {
                         return 0;
                     }
                     // The name didn't match so we now check if the type we're looking for is inside
                     // the generics!
-                    lev = Math.min(lev, checkIfInGenerics(row, elem, maxLevDistance));
-                    return lev;
-                } else if (lev > maxLevDistance) {
+                    dist = Math.min(dist, checkIfInGenerics(row, elem, maxEditDistance));
+                    return dist;
+                } else if (dist > maxEditDistance) {
                     // So our item's name doesn't match at all and has generics.
                     //
                     // Maybe it's present in a sub generic? For example "f<A<B<C>>>()", if we're
                     // looking for "B<C>", we'll need to go down.
-                    return checkIfInGenerics(row, elem, maxLevDistance);
+                    return checkIfInGenerics(row, elem, maxEditDistance);
                 } else {
                     // At this point, the name kinda match and we have generics to check, so
                     // let's go!
-                    const tmp_lev = checkGenerics(row, elem, lev, maxLevDistance);
-                    if (tmp_lev > maxLevDistance) {
-                        return maxLevDistance + 1;
+                    const tmp_dist = checkGenerics(row, elem, dist, maxEditDistance);
+                    if (tmp_dist > maxEditDistance) {
+                        return maxEditDistance + 1;
                     }
                     // We compute the median value of both checks and return it.
-                    return (tmp_lev + lev) / 2;
+                    return (tmp_dist + dist) / 2;
                 }
             } else if (elem.generics.length > 0) {
                 // In this case, we were expecting generics but there isn't so we simply reject this
                 // one.
-                return maxLevDistance + 1;
+                return maxEditDistance + 1;
             }
             // No generics on our query or on the target type so we can return without doing
             // anything else.
-            return lev;
+            return dist;
         }
 
         /**
@@ -1122,27 +1203,27 @@ function initSearch(rawSearchIndex) {
          * @param {QueryElement} elem    - The element from the parsed query.
          * @param {integer} typeFilter
          *
-         * @return {integer} - Returns a Levenshtein distance to the best match. If there is no
-         *                      match, returns `maxLevDistance + 1`.
+         * @return {integer} - Returns an edit distance to the best match. If there is no
+         *                      match, returns `maxEditDistance + 1`.
          */
-        function findArg(row, elem, typeFilter, maxLevDistance) {
-            let lev = maxLevDistance + 1;
+        function findArg(row, elem, typeFilter, maxEditDistance) {
+            let dist = maxEditDistance + 1;
 
             if (row && row.type && row.type.inputs && row.type.inputs.length > 0) {
                 for (const input of row.type.inputs) {
                     if (!typePassesFilter(typeFilter, input.ty)) {
                         continue;
                     }
-                    lev = Math.min(
-                        lev,
-                        checkType(input, elem, parsedQuery.literalSearch, maxLevDistance)
+                    dist = Math.min(
+                        dist,
+                        checkType(input, elem, parsedQuery.literalSearch, maxEditDistance)
                     );
-                    if (lev === 0) {
+                    if (dist === 0) {
                         return 0;
                     }
                 }
             }
-            return parsedQuery.literalSearch ? maxLevDistance + 1 : lev;
+            return parsedQuery.literalSearch ? maxEditDistance + 1 : dist;
         }
 
         /**
@@ -1152,11 +1233,11 @@ function initSearch(rawSearchIndex) {
          * @param {QueryElement} elem   - The element from the parsed query.
          * @param {integer} typeFilter
          *
-         * @return {integer} - Returns a Levenshtein distance to the best match. If there is no
-         *                      match, returns `maxLevDistance + 1`.
+         * @return {integer} - Returns an edit distance to the best match. If there is no
+         *                      match, returns `maxEditDistance + 1`.
          */
-        function checkReturned(row, elem, typeFilter, maxLevDistance) {
-            let lev = maxLevDistance + 1;
+        function checkReturned(row, elem, typeFilter, maxEditDistance) {
+            let dist = maxEditDistance + 1;
 
             if (row && row.type && row.type.output.length > 0) {
                 const ret = row.type.output;
@@ -1164,23 +1245,23 @@ function initSearch(rawSearchIndex) {
                     if (!typePassesFilter(typeFilter, ret_ty.ty)) {
                         continue;
                     }
-                    lev = Math.min(
-                        lev,
-                        checkType(ret_ty, elem, parsedQuery.literalSearch, maxLevDistance)
+                    dist = Math.min(
+                        dist,
+                        checkType(ret_ty, elem, parsedQuery.literalSearch, maxEditDistance)
                     );
-                    if (lev === 0) {
+                    if (dist === 0) {
                         return 0;
                     }
                 }
             }
-            return parsedQuery.literalSearch ? maxLevDistance + 1 : lev;
+            return parsedQuery.literalSearch ? maxEditDistance + 1 : dist;
         }
 
-        function checkPath(contains, ty, maxLevDistance) {
+        function checkPath(contains, ty, maxEditDistance) {
             if (contains.length === 0) {
                 return 0;
             }
-            let ret_lev = maxLevDistance + 1;
+            let ret_dist = maxEditDistance + 1;
             const path = ty.path.split("::");
 
             if (ty.parent && ty.parent.name) {
@@ -1190,27 +1271,27 @@ function initSearch(rawSearchIndex) {
             const length = path.length;
             const clength = contains.length;
             if (clength > length) {
-                return maxLevDistance + 1;
+                return maxEditDistance + 1;
             }
             for (let i = 0; i < length; ++i) {
                 if (i + clength > length) {
                     break;
                 }
-                let lev_total = 0;
+                let dist_total = 0;
                 let aborted = false;
                 for (let x = 0; x < clength; ++x) {
-                    const lev = levenshtein(path[i + x], contains[x]);
-                    if (lev > maxLevDistance) {
+                    const dist = editDistance(path[i + x], contains[x], maxEditDistance);
+                    if (dist > maxEditDistance) {
                         aborted = true;
                         break;
                     }
-                    lev_total += lev;
+                    dist_total += dist;
                 }
                 if (!aborted) {
-                    ret_lev = Math.min(ret_lev, Math.round(lev_total / clength));
+                    ret_dist = Math.min(ret_dist, Math.round(dist_total / clength));
                 }
             }
-            return ret_lev;
+            return ret_dist;
         }
 
         function typePassesFilter(filter, type) {
@@ -1244,6 +1325,7 @@ function initSearch(rawSearchIndex) {
                 parent: item.parent,
                 type: item.type,
                 is_alias: true,
+                deprecated: item.deprecated,
             };
         }
 
@@ -1304,31 +1386,31 @@ function initSearch(rawSearchIndex) {
          * This function adds the given result into the provided `results` map if it matches the
          * following condition:
          *
-         * * If it is a "literal search" (`parsedQuery.literalSearch`), then `lev` must be 0.
-         * * If it is not a "literal search", `lev` must be <= `maxLevDistance`.
+         * * If it is a "literal search" (`parsedQuery.literalSearch`), then `dist` must be 0.
+         * * If it is not a "literal search", `dist` must be <= `maxEditDistance`.
          *
          * The `results` map contains information which will be used to sort the search results:
          *
          * * `fullId` is a `string`` used as the key of the object we use for the `results` map.
          * * `id` is the index in both `searchWords` and `searchIndex` arrays for this element.
          * * `index` is an `integer`` used to sort by the position of the word in the item's name.
-         * * `lev` is the main metric used to sort the search results.
-         * * `path_lev` is zero if a single-component search query is used, otherwise it's the
+         * * `dist` is the main metric used to sort the search results.
+         * * `path_dist` is zero if a single-component search query is used, otherwise it's the
          *   distance computed for everything other than the last path component.
          *
          * @param {Results} results
          * @param {string} fullId
          * @param {integer} id
          * @param {integer} index
-         * @param {integer} lev
-         * @param {integer} path_lev
+         * @param {integer} dist
+         * @param {integer} path_dist
          */
-        function addIntoResults(results, fullId, id, index, lev, path_lev, maxLevDistance) {
-            const inBounds = lev <= maxLevDistance || index !== -1;
-            if (lev === 0 || (!parsedQuery.literalSearch && inBounds)) {
+        function addIntoResults(results, fullId, id, index, dist, path_dist, maxEditDistance) {
+            const inBounds = dist <= maxEditDistance || index !== -1;
+            if (dist === 0 || (!parsedQuery.literalSearch && inBounds)) {
                 if (results[fullId] !== undefined) {
                     const result = results[fullId];
-                    if (result.dontValidate || result.lev <= lev) {
+                    if (result.dontValidate || result.dist <= dist) {
                         return;
                     }
                 }
@@ -1336,8 +1418,8 @@ function initSearch(rawSearchIndex) {
                     id: id,
                     index: index,
                     dontValidate: parsedQuery.literalSearch,
-                    lev: lev,
-                    path_lev: path_lev,
+                    dist: dist,
+                    path_dist: path_dist,
                 };
             }
         }
@@ -1346,7 +1428,7 @@ function initSearch(rawSearchIndex) {
          * This function is called in case the query is only one element (with or without generics).
          * This element will be compared to arguments' and returned values' items and also to items.
          *
-         * Other important thing to note: since there is only one element, we use levenshtein
+         * Other important thing to note: since there is only one element, we use edit
          * distance for name comparisons.
          *
          * @param {Row} row
@@ -1364,22 +1446,22 @@ function initSearch(rawSearchIndex) {
             results_others,
             results_in_args,
             results_returned,
-            maxLevDistance
+            maxEditDistance
         ) {
             if (!row || (filterCrates !== null && row.crate !== filterCrates)) {
                 return;
             }
-            let lev, index = -1, path_lev = 0;
+            let dist, index = -1, path_dist = 0;
             const fullId = row.id;
             const searchWord = searchWords[pos];
 
-            const in_args = findArg(row, elem, parsedQuery.typeFilter, maxLevDistance);
-            const returned = checkReturned(row, elem, parsedQuery.typeFilter, maxLevDistance);
+            const in_args = findArg(row, elem, parsedQuery.typeFilter, maxEditDistance);
+            const returned = checkReturned(row, elem, parsedQuery.typeFilter, maxEditDistance);
 
-            // path_lev is 0 because no parent path information is currently stored
+            // path_dist is 0 because no parent path information is currently stored
             // in the search index
-            addIntoResults(results_in_args, fullId, pos, -1, in_args, 0, maxLevDistance);
-            addIntoResults(results_returned, fullId, pos, -1, returned, 0, maxLevDistance);
+            addIntoResults(results_in_args, fullId, pos, -1, in_args, 0, maxEditDistance);
+            addIntoResults(results_returned, fullId, pos, -1, returned, 0, maxEditDistance);
 
             if (!typePassesFilter(parsedQuery.typeFilter, row.ty)) {
                 return;
@@ -1403,34 +1485,34 @@ function initSearch(rawSearchIndex) {
             // No need to check anything else if it's a "pure" generics search.
             if (elem.name.length === 0) {
                 if (row.type !== null) {
-                    lev = checkGenerics(row.type, elem, maxLevDistance + 1, maxLevDistance);
-                    // path_lev is 0 because we know it's empty
-                    addIntoResults(results_others, fullId, pos, index, lev, 0, maxLevDistance);
+                    dist = checkGenerics(row.type, elem, maxEditDistance + 1, maxEditDistance);
+                    // path_dist is 0 because we know it's empty
+                    addIntoResults(results_others, fullId, pos, index, dist, 0, maxEditDistance);
                 }
                 return;
             }
 
             if (elem.fullPath.length > 1) {
-                path_lev = checkPath(elem.pathWithoutLast, row, maxLevDistance);
-                if (path_lev > maxLevDistance) {
+                path_dist = checkPath(elem.pathWithoutLast, row, maxEditDistance);
+                if (path_dist > maxEditDistance) {
                     return;
                 }
             }
 
             if (parsedQuery.literalSearch) {
                 if (searchWord === elem.name) {
-                    addIntoResults(results_others, fullId, pos, index, 0, path_lev);
+                    addIntoResults(results_others, fullId, pos, index, 0, path_dist);
                 }
                 return;
             }
 
-            lev = levenshtein(searchWord, elem.pathLast);
+            dist = editDistance(searchWord, elem.pathLast, maxEditDistance);
 
-            if (index === -1 && lev + path_lev > maxLevDistance) {
+            if (index === -1 && dist + path_dist > maxEditDistance) {
                 return;
             }
 
-            addIntoResults(results_others, fullId, pos, index, lev, path_lev, maxLevDistance);
+            addIntoResults(results_others, fullId, pos, index, dist, path_dist, maxEditDistance);
         }
 
         /**
@@ -1442,22 +1524,22 @@ function initSearch(rawSearchIndex) {
          * @param {integer} pos      - Position in the `searchIndex`.
          * @param {Object} results
          */
-        function handleArgs(row, pos, results, maxLevDistance) {
+        function handleArgs(row, pos, results, maxEditDistance) {
             if (!row || (filterCrates !== null && row.crate !== filterCrates)) {
                 return;
             }
 
-            let totalLev = 0;
-            let nbLev = 0;
+            let totalDist = 0;
+            let nbDist = 0;
 
             // If the result is too "bad", we return false and it ends this search.
             function checkArgs(elems, callback) {
                 for (const elem of elems) {
                     // There is more than one parameter to the query so all checks should be "exact"
-                    const lev = callback(row, elem, NO_TYPE_FILTER, maxLevDistance);
-                    if (lev <= 1) {
-                        nbLev += 1;
-                        totalLev += lev;
+                    const dist = callback(row, elem, NO_TYPE_FILTER, maxEditDistance);
+                    if (dist <= 1) {
+                        nbDist += 1;
+                        totalDist += dist;
                     } else {
                         return false;
                     }
@@ -1471,11 +1553,11 @@ function initSearch(rawSearchIndex) {
                 return;
             }
 
-            if (nbLev === 0) {
+            if (nbDist === 0) {
                 return;
             }
-            const lev = Math.round(totalLev / nbLev);
-            addIntoResults(results, row.id, pos, 0, lev, 0, maxLevDistance);
+            const dist = Math.round(totalDist / nbDist);
+            addIntoResults(results, row.id, pos, 0, dist, 0, maxEditDistance);
         }
 
         function innerRunQuery() {
@@ -1488,7 +1570,7 @@ function initSearch(rawSearchIndex) {
             for (const elem of parsedQuery.returned) {
                 queryLen += elem.name.length;
             }
-            const maxLevDistance = Math.floor(queryLen / 3);
+            const maxEditDistance = Math.floor(queryLen / 3);
 
             if (parsedQuery.foundElems === 1) {
                 if (parsedQuery.elems.length === 1) {
@@ -1503,7 +1585,7 @@ function initSearch(rawSearchIndex) {
                             results_others,
                             results_in_args,
                             results_returned,
-                            maxLevDistance
+                            maxEditDistance
                         );
                     }
                 } else if (parsedQuery.returned.length === 1) {
@@ -1515,14 +1597,14 @@ function initSearch(rawSearchIndex) {
                             row,
                             elem,
                             parsedQuery.typeFilter,
-                            maxLevDistance
+                            maxEditDistance
                         );
-                        addIntoResults(results_others, row.id, i, -1, in_returned, maxLevDistance);
+                        addIntoResults(results_others, row.id, i, -1, in_returned, maxEditDistance);
                     }
                 }
             } else if (parsedQuery.foundElems > 0) {
                 for (i = 0, nSearchWords = searchWords.length; i < nSearchWords; ++i) {
-                    handleArgs(searchIndex[i], i, results_others, maxLevDistance);
+                    handleArgs(searchIndex[i], i, results_others, maxEditDistance);
                 }
             }
         }
@@ -1560,7 +1642,7 @@ function initSearch(rawSearchIndex) {
      *
      * @return {boolean}       - Whether the result is valid or not
      */
-    function validateResult(name, path, keys, parent, maxLevDistance) {
+    function validateResult(name, path, keys, parent, maxEditDistance) {
         if (!keys || !keys.length) {
             return true;
         }
@@ -1574,8 +1656,8 @@ function initSearch(rawSearchIndex) {
                 // next if there is a parent, check for exact parent match
                 (parent !== undefined && parent.name !== undefined &&
                     parent.name.toLowerCase().indexOf(key) > -1) ||
-                // lastly check to see if the name was a levenshtein match
-                levenshtein(name, key) <= maxLevDistance)) {
+                // lastly check to see if the name was an editDistance match
+                editDistance(name, key, maxEditDistance) <= maxEditDistance)) {
                 return false;
             }
         }
@@ -2064,10 +2146,11 @@ function initSearch(rawSearchIndex) {
              *   n: Array<string>,
              *   t: String,
              *   d: Array<string>,
-             *   q: Array<string>,
+             *   q: Array<[Number, string]>,
              *   i: Array<Number>,
              *   f: Array<RawFunctionSearchType>,
              *   p: Array<Object>,
+             *   c: Array<Number>
              * }}
              */
             const crateCorpus = rawSearchIndex[crate];
@@ -2086,6 +2169,7 @@ function initSearch(rawSearchIndex) {
                 type: null,
                 id: id,
                 normalizedName: crate.indexOf("_") === -1 ? crate : crate.replace(/_/g, ""),
+                deprecated: null,
             };
             id += 1;
             searchIndex.push(crateRow);
@@ -2095,14 +2179,20 @@ function initSearch(rawSearchIndex) {
             const itemTypes = crateCorpus.t;
             // an array of (String) item names
             const itemNames = crateCorpus.n;
-            // an array of (String) full paths (or empty string for previous path)
-            const itemPaths = crateCorpus.q;
+            // an array of [(Number) item index,
+            //              (String) full path]
+            // an item whose index is not present will fall back to the previous present path
+            // i.e. if indices 4 and 11 are present, but 5-10 and 12-13 are not present,
+            // 5-10 will fall back to the path for 4 and 12-13 will fall back to the path for 11
+            const itemPaths = new Map(crateCorpus.q);
             // an array of (String) descriptions
             const itemDescs = crateCorpus.d;
             // an array of (Number) the parent path index + 1 to `paths`, or 0 if none
             const itemParentIdxs = crateCorpus.i;
             // an array of (Object | null) the type of the function, if any
             const itemFunctionSearchTypes = crateCorpus.f;
+            // an array of (Number) indices for the deprecated items
+            const deprecatedItems = new Set(crateCorpus.c);
             // an array of [(Number) item type,
             //              (String) name]
             const paths = crateCorpus.p;
@@ -2142,12 +2232,13 @@ function initSearch(rawSearchIndex) {
                     crate: crate,
                     ty: itemTypes.charCodeAt(i) - charA,
                     name: itemNames[i],
-                    path: itemPaths[i] ? itemPaths[i] : lastPath,
+                    path: itemPaths.has(i) ? itemPaths.get(i) : lastPath,
                     desc: itemDescs[i],
                     parent: itemParentIdxs[i] > 0 ? paths[itemParentIdxs[i] - 1] : undefined,
                     type: buildFunctionSearchType(itemFunctionSearchTypes[i], lowercasePaths),
                     id: id,
                     normalizedName: word.indexOf("_") === -1 ? word : word.replace(/_/g, ""),
+                    deprecated: deprecatedItems.has(i),
                 };
                 id += 1;
                 searchIndex.push(row);
diff --git a/src/librustdoc/html/templates/item_info.html b/src/librustdoc/html/templates/item_info.html
new file mode 100644
index 00000000000..d2ea9bdae9c
--- /dev/null
+++ b/src/librustdoc/html/templates/item_info.html
@@ -0,0 +1,7 @@
+{% if !items.is_empty() %}
+    <span class="item-info"> {# #}
+        {% for item in items %}
+            {{item|safe}} {# #}
+        {% endfor %}
+    </span>
+{% endif %}
diff --git a/src/librustdoc/html/templates/short_item_info.html b/src/librustdoc/html/templates/short_item_info.html
new file mode 100644
index 00000000000..e3125af0e47
--- /dev/null
+++ b/src/librustdoc/html/templates/short_item_info.html
@@ -0,0 +1,23 @@
+{% match self %}
+    {% when Self::Deprecation with { message } %}
+        <div class="stab deprecated"> {# #}
+            <span class="emoji">👎</span> {# #}
+            <span>{{message}}</span> {# #}
+        </div> {# #}
+    {% when Self::Unstable with { feature, tracking } %}
+        <div class="stab unstable"> {# #}
+            <span class="emoji">🔬</span> {# #}
+            <span> {# #}
+                This is a nightly-only experimental API. ({# #}
+                <code>{{feature}}</code> {# #}
+                {% match tracking %}
+                    {% when Some with ((url, num)) %}
+                        &nbsp;<a href="{{url}}{{num}}">#{{num}}</a> {# #}
+                    {% when None %}
+                {% endmatch %}
+                ) {# #}
+            </span> {# #}
+        </div> {# #}
+    {% when Self::Portability with { message } %}
+        <div class="stab portability">{{message|safe}}</div> {# #}
+{% endmatch %}
diff --git a/src/librustdoc/html/templates/sidebar.html b/src/librustdoc/html/templates/sidebar.html
new file mode 100644
index 00000000000..01d476ad29f
--- /dev/null
+++ b/src/librustdoc/html/templates/sidebar.html
@@ -0,0 +1,37 @@
+{% if !title.is_empty() %}
+    <h2 class="location"> {# #}
+        <a href="#">{{title_prefix}}{{title}}</a> {# #}
+    </h2>
+{% endif %}
+<div class="sidebar-elems">
+    {% if is_crate %}
+        <ul class="block">
+            {% if !version.is_empty() %}
+                <li class="version">Version {{+ version}}</li>
+            {% endif %}
+            <li><a id="all-types" href="all.html">All Items</a></li> {# #}
+        </ul>
+    {% endif %}
+
+    {% if self.should_render_blocks() %}
+        <section>
+            {% for block in blocks %}
+                {% if block.should_render() %}
+                    {% if !block.heading.name.is_empty() %}
+                        <h3><a href="#{{block.heading.href|safe}}">{{block.heading.name}}</a></h3>
+                    {% endif %}
+                    {% if !block.links.is_empty() %}
+                        <ul class="block">
+                            {% for link in block.links %}
+                                <li><a href="#{{link.href|safe}}">{{link.name}}</a></li>
+                            {% endfor %}
+                        </ul>
+                    {% endif %}
+                {% endif %}
+            {% endfor %}
+        </section>
+    {% endif %}
+    {% if !path.is_empty() %}
+        <h2><a href="index.html">In {{+ path}}</a></h2>
+    {% endif %}
+</div>
diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs
index bcb69d1a4ca..358f6ad566c 100644
--- a/src/librustdoc/passes/collect_intra_doc_links.rs
+++ b/src/librustdoc/passes/collect_intra_doc_links.rs
@@ -13,7 +13,7 @@ use rustc_hir::def::Namespace::*;
 use rustc_hir::def::{DefKind, Namespace, PerNS};
 use rustc_hir::def_id::{DefId, CRATE_DEF_ID};
 use rustc_hir::Mutability;
-use rustc_middle::ty::{Ty, TyCtxt};
+use rustc_middle::ty::{fast_reject::TreatProjections, Ty, TyCtxt};
 use rustc_middle::{bug, ty};
 use rustc_resolve::rustdoc::MalformedGenerics;
 use rustc_resolve::rustdoc::{prepare_to_doc_link_resolution, strip_generics_from_path};
@@ -735,7 +735,7 @@ fn trait_impls_for<'a>(
         trace!("considering explicit impl for trait {:?}", trait_);
 
         // Look at each trait implementation to see if it's an impl for `did`
-        tcx.find_map_relevant_impl(trait_, ty, |impl_| {
+        tcx.find_map_relevant_impl(trait_, ty, TreatProjections::ForLookup, |impl_| {
             let trait_ref = tcx.impl_trait_ref(impl_).expect("this is not an inherent impl");
             // Check if these are the same type.
             let impl_type = trait_ref.skip_binder().self_ty();
diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs
index 44e9b49f82a..e09a68069e8 100644
--- a/src/librustdoc/visit_ast.rs
+++ b/src/librustdoc/visit_ast.rs
@@ -224,6 +224,11 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
     ) -> bool {
         debug!("maybe_inline_local res: {:?}", res);
 
+        if renamed == Some(kw::Underscore) {
+            // We never inline `_` reexports.
+            return false;
+        }
+
         if self.cx.output_format.is_json() {
             return false;
         }
@@ -346,8 +351,8 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
                     self.visit_foreign_item_inner(item, None);
                 }
             }
-            // If we're inlining, skip private items or item reexported as "_".
-            _ if self.inlining && (!is_pub || renamed == Some(kw::Underscore)) => {}
+            // If we're inlining, skip private items.
+            _ if self.inlining && !is_pub => {}
             hir::ItemKind::GlobalAsm(..) => {}
             hir::ItemKind::Use(_, hir::UseKind::ListStem) => {}
             hir::ItemKind::Use(path, kind) => {
diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md
index 765826ed867..0abe234fc8f 100644
--- a/src/tools/clippy/CHANGELOG.md
+++ b/src/tools/clippy/CHANGELOG.md
@@ -6,11 +6,156 @@ document.
 
 ## Unreleased / Beta / In Rust Nightly
 
-[d822110d...master](https://github.com/rust-lang/rust-clippy/compare/d822110d...master)
+[7f27e2e7...master](https://github.com/rust-lang/rust-clippy/compare/7f27e2e7...master)
+
+## Rust 1.68
+
+Current stable, released 2023-03-09
+
+[d822110d...7f27e2e7](https://github.com/rust-lang/rust-clippy/compare/d822110d...7f27e2e7)
+
+### New Lints
+
+* [`permissions_set_readonly_false`]
+  [#10063](https://github.com/rust-lang/rust-clippy/pull/10063)
+* [`almost_complete_range`]
+  [#10043](https://github.com/rust-lang/rust-clippy/pull/10043)
+* [`size_of_ref`]
+  [#10098](https://github.com/rust-lang/rust-clippy/pull/10098)
+* [`semicolon_outside_block`]
+  [#9826](https://github.com/rust-lang/rust-clippy/pull/9826)
+* [`semicolon_inside_block`]
+  [#9826](https://github.com/rust-lang/rust-clippy/pull/9826)
+* [`transmute_null_to_fn`]
+  [#10099](https://github.com/rust-lang/rust-clippy/pull/10099)
+* [`fn_null_check`]
+  [#10099](https://github.com/rust-lang/rust-clippy/pull/10099)
+
+### Moves and Deprecations
+
+* Moved [`manual_clamp`] to `nursery` (Now allow-by-default)
+  [#10101](https://github.com/rust-lang/rust-clippy/pull/10101)
+* Moved [`mutex_atomic`] to `restriction`
+  [#10115](https://github.com/rust-lang/rust-clippy/pull/10115)
+* Renamed `derive_hash_xor_eq` to [`derived_hash_with_manual_eq`]
+  [#10184](https://github.com/rust-lang/rust-clippy/pull/10184)
+
+### Enhancements
+
+* [`collapsible_str_replace`]: Now takes MSRV into consideration. The minimal version is 1.58
+  [#10047](https://github.com/rust-lang/rust-clippy/pull/10047)
+* [`unused_self`]: No longer lints, if the method body contains a `todo!()` call
+  [#10166](https://github.com/rust-lang/rust-clippy/pull/10166)
+* [`derivable_impls`]: Now suggests deriving `Default` for enums with default unit variants
+  [#10161](https://github.com/rust-lang/rust-clippy/pull/10161)
+* [`arithmetic_side_effects`]: Added two new config values
+  `arithmetic-side-effects-allowed-binary` and `arithmetic-side-effects-allowed-unary`
+  to allow operation on user types
+  [#9840](https://github.com/rust-lang/rust-clippy/pull/9840)
+* [`large_const_arrays`], [`large_stack_arrays`]: avoid integer overflow when calculating
+  total array size
+  [#10103](https://github.com/rust-lang/rust-clippy/pull/10103)
+* [`indexing_slicing`]: add new config `suppress-restriction-lint-in-const` to enable
+  restriction lints, even if the suggestion might not be applicable
+  [#9920](https://github.com/rust-lang/rust-clippy/pull/9920)
+* [`needless_borrow`], [`redundant_clone`]: Now track references better and detect more cases
+  [#9701](https://github.com/rust-lang/rust-clippy/pull/9701)
+* [`derived_hash_with_manual_eq`]: Now allows `#[derive(PartialEq)]` with custom `Hash`
+  implementations
+  [#10184](https://github.com/rust-lang/rust-clippy/pull/10184)
+* [`manual_is_ascii_check`]: Now detects ranges with `.contains()` calls
+  [#10053](https://github.com/rust-lang/rust-clippy/pull/10053)
+* [`transmuting_null`]: Now detects `const` pointers to all types
+  [#10099](https://github.com/rust-lang/rust-clippy/pull/10099)
+* [`needless_return`]: Now detects more cases for returns of owned values
+  [#10110](https://github.com/rust-lang/rust-clippy/pull/10110)
+
+### False Positive Fixes
+
+* [`field_reassign_with_default`]: No longer lints cases, where values are initializes from
+  closures capturing struct values
+  [#10143](https://github.com/rust-lang/rust-clippy/pull/10143)
+* [`seek_to_start_instead_of_rewind`]: No longer lints, if the return of `seek` is used.
+  [#10096](https://github.com/rust-lang/rust-clippy/pull/10096)
+* [`manual_filter`]: Now ignores if expressions where the else branch has side effects or
+  doesn't return `None`
+  [#10091](https://github.com/rust-lang/rust-clippy/pull/10091)
+* [`implicit_clone`]: No longer lints if the type doesn't implement clone
+  [#10022](https://github.com/rust-lang/rust-clippy/pull/10022)
+* [`match_wildcard_for_single_variants`]: No longer lints on wildcards with a guard
+  [#10056](https://github.com/rust-lang/rust-clippy/pull/10056)
+* [`drop_ref`]: No longer lints idiomatic expression in `match` arms
+  [#10142](https://github.com/rust-lang/rust-clippy/pull/10142)
+* [`arithmetic_side_effects`]: No longer lints on corner cases with negative number literals
+  [#9867](https://github.com/rust-lang/rust-clippy/pull/9867)
+* [`string_lit_as_bytes`]: No longer lints in scrutinies of `match` statements
+  [#10012](https://github.com/rust-lang/rust-clippy/pull/10012)
+* [`manual_assert`]: No longer lints in `else if` statements
+  [#10013](https://github.com/rust-lang/rust-clippy/pull/10013)
+* [`needless_return`]: don't lint when using `do yeet`
+  [#10109](https://github.com/rust-lang/rust-clippy/pull/10109)
+* All lints: No longer lint in enum discriminant values when the suggestion won't work in a
+  const context
+  [#10008](https://github.com/rust-lang/rust-clippy/pull/10008)
+* [`single_element_loop`]: No longer lints, if the loop contains a `break` or `continue`
+  [#10162](https://github.com/rust-lang/rust-clippy/pull/10162)
+* [`uninlined_format_args`]: No longer suggests inlining arguments in `assert!` and
+  `debug_assert!` macros before 2021 edition
+  [#10055](https://github.com/rust-lang/rust-clippy/pull/10055)
+* [`explicit_counter_loop`]: No longer ignores counter changes after `continue` expressions
+  [#10094](https://github.com/rust-lang/rust-clippy/pull/10094)
+* [`from_over_into`]: No longer lints on opaque types
+  [#9982](https://github.com/rust-lang/rust-clippy/pull/9982)
+* [`expl_impl_clone_on_copy`]: No longer lints on `#[repr(packed)]` structs with generic
+  parameters
+  [#10189](https://github.com/rust-lang/rust-clippy/pull/10189)
+
+### Suggestion Fixes/Improvements
+
+* [`zero_ptr`]: Now suggests `core::` paths for `no_std` crates
+  [#10023](https://github.com/rust-lang/rust-clippy/pull/10023)
+* [`useless_conversion`]: Now suggests removing calls to `into_iter()` on an expression
+  implementing `Iterator`
+  [#10020](https://github.com/rust-lang/rust-clippy/pull/10020)
+* [`box_default`]: The suggestion now uses short paths
+  [#10153](https://github.com/rust-lang/rust-clippy/pull/10153)
+* [`default_trait_access`], [`clone_on_copy`]: The suggestion now uses short paths
+  [#10160](https://github.com/rust-lang/rust-clippy/pull/10160)
+* [`comparison_to_empty`]: The suggestion now removes unused deref operations
+  [#9962](https://github.com/rust-lang/rust-clippy/pull/9962)
+* [`manual_let_else`]: Suggestions for or-patterns now include required brackets.
+  [#9966](https://github.com/rust-lang/rust-clippy/pull/9966)
+* [`match_single_binding`]: suggestion no longer introduces unneeded semicolons
+  [#10060](https://github.com/rust-lang/rust-clippy/pull/10060)
+* [`case_sensitive_file_extension_comparisons`]: Now displays a suggestion with `Path`
+  [#10107](https://github.com/rust-lang/rust-clippy/pull/10107)
+* [`empty_structs_with_brackets`]: The suggestion is no longer machine applicable, to avoid
+  errors when accessing struct fields
+  [#10141](https://github.com/rust-lang/rust-clippy/pull/10141)
+* [`identity_op`]: Removes borrows in the suggestion when needed
+  [#10004](https://github.com/rust-lang/rust-clippy/pull/10004)
+* [`suboptimal_flops`]: The suggestion now includes parentheses when required
+  [#10113](https://github.com/rust-lang/rust-clippy/pull/10113)
+* [`iter_kv_map`]: Now handles `mut` and reference annotations in the suggestion
+  [#10159](https://github.com/rust-lang/rust-clippy/pull/10159)
+* [`redundant_static_lifetimes`]: The suggestion no longer removes `mut` from references
+  [#10006](https://github.com/rust-lang/rust-clippy/pull/10006)
+
+### ICE Fixes
+
+* [`new_ret_no_self`]: Now avoids a stack overflow for `impl Trait` types
+  [#10086](https://github.com/rust-lang/rust-clippy/pull/10086)
+* [`unnecessary_to_owned`]: Now handles compiler generated notes better
+  [#10027](https://github.com/rust-lang/rust-clippy/pull/10027)
+
+### Others
+
+* `SYSROOT` and `--sysroot` can now be set at the same time
+  [#10149](https://github.com/rust-lang/rust-clippy/pull/10149)
 
 ## Rust 1.67
 
-Current stable, released 2023-01-26
+Released 2023-01-26
 
 [4f142aa1...d822110d](https://github.com/rust-lang/rust-clippy/compare/4f142aa1...d822110d)
 
@@ -4307,6 +4452,7 @@ Released 2018-09-13
 [`collapsible_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if
 [`collapsible_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_match
 [`collapsible_str_replace`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_str_replace
+[`collection_is_never_read`]: https://rust-lang.github.io/rust-clippy/master/index.html#collection_is_never_read
 [`comparison_chain`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_chain
 [`comparison_to_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_to_empty
 [`const_static_lifetime`]: https://rust-lang.github.io/rust-clippy/master/index.html#const_static_lifetime
@@ -4497,6 +4643,7 @@ Released 2018-09-13
 [`let_underscore_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_must_use
 [`let_underscore_untyped`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_untyped
 [`let_unit_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_unit_value
+[`let_with_type_underscore`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_with_type_underscore
 [`linkedlist`]: https://rust-lang.github.io/rust-clippy/master/index.html#linkedlist
 [`logic_bug`]: https://rust-lang.github.io/rust-clippy/master/index.html#logic_bug
 [`lossy_float_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#lossy_float_literal
@@ -4560,6 +4707,7 @@ Released 2018-09-13
 [`mismatching_type_param_order`]: https://rust-lang.github.io/rust-clippy/master/index.html#mismatching_type_param_order
 [`misnamed_getters`]: https://rust-lang.github.io/rust-clippy/master/index.html#misnamed_getters
 [`misrefactored_assign_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#misrefactored_assign_op
+[`missing_assert_message`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_assert_message
 [`missing_const_for_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn
 [`missing_docs_in_private_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_docs_in_private_items
 [`missing_enforced_import_renames`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_enforced_import_renames
@@ -4689,6 +4837,7 @@ Released 2018-09-13
 [`read_zero_byte_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#read_zero_byte_vec
 [`recursive_format_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#recursive_format_impl
 [`redundant_allocation`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_allocation
+[`redundant_async_block`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_async_block
 [`redundant_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_clone
 [`redundant_closure`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure
 [`redundant_closure_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure_call
diff --git a/src/tools/clippy/COPYRIGHT b/src/tools/clippy/COPYRIGHT
index a6be75b5e31..82703b18fd7 100644
--- a/src/tools/clippy/COPYRIGHT
+++ b/src/tools/clippy/COPYRIGHT
@@ -1,3 +1,5 @@
+// REUSE-IgnoreStart
+
 Copyright 2014-2022 The Rust Project Developers
 
 Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
@@ -5,3 +7,5 @@ http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
 <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
 option. All files in the project carrying such notice may not be
 copied, modified, or distributed except according to those terms.
+
+// REUSE-IgnoreEnd
diff --git a/src/tools/clippy/Cargo.toml b/src/tools/clippy/Cargo.toml
index 70d1268090f..c35dfcbd8c4 100644
--- a/src/tools/clippy/Cargo.toml
+++ b/src/tools/clippy/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "clippy"
-version = "0.1.69"
+version = "0.1.70"
 description = "A bunch of helpful lints to avoid common pitfalls in Rust"
 repository = "https://github.com/rust-lang/rust-clippy"
 readme = "README.md"
diff --git a/src/tools/clippy/README.md b/src/tools/clippy/README.md
index 3e7379ace7e..b69ed8900a4 100644
--- a/src/tools/clippy/README.md
+++ b/src/tools/clippy/README.md
@@ -275,6 +275,8 @@ If you want to contribute to Clippy, you can find more information in [CONTRIBUT
 
 ## License
 
+<!-- REUSE-IgnoreStart -->
+
 Copyright 2014-2022 The Rust Project Developers
 
 Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
@@ -282,3 +284,5 @@ Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
 <LICENSE-MIT or [https://opensource.org/licenses/MIT](https://opensource.org/licenses/MIT)>, at your
 option. Files in the project may not be
 copied, modified, or distributed except according to those terms.
+
+<!-- REUSE-IgnoreEnd -->
diff --git a/src/tools/clippy/book/src/development/proposals/syntax-tree-patterns.md b/src/tools/clippy/book/src/development/proposals/syntax-tree-patterns.md
index c5587c4bf90..ea4978011b1 100644
--- a/src/tools/clippy/book/src/development/proposals/syntax-tree-patterns.md
+++ b/src/tools/clippy/book/src/development/proposals/syntax-tree-patterns.md
@@ -68,13 +68,13 @@ The second part of the motivation is clippy's dependence on unstable
 compiler-internal data structures. Clippy lints are currently written against
 the compiler's AST / HIR which means that even small changes in these data
 structures might break a lot of lints. The second goal of this RFC is to **make
-lints independant of the compiler's AST / HIR data structures**.
+lints independent of the compiler's AST / HIR data structures**.
 
 # Approach
 
 A lot of complexity in writing lints currently seems to come from having to
 manually implement the matching logic (see code samples above). It's an
-imparative style that describes *how* to match a syntax tree node instead of
+imperative style that describes *how* to match a syntax tree node instead of
 specifying *what* should be matched against declaratively. In other areas, it's
 common to use declarative patterns to describe desired information and let the
 implementation do the actual matching. A well-known example of this approach are
@@ -270,7 +270,7 @@ pattern!{
     // matches if expressions that **may or may not** have an else block
     // Attn: `If(_, _, _)` matches only ifs that **have** an else block
     //
-    //              | if with else block | if witout else block
+    //              | if with else block | if without else block
     // If(_, _, _)  |       match        |       no match
     // If(_, _, _?) |       match        |        match
     // If(_, _, ()) |      no match      |        match
@@ -568,7 +568,7 @@ another example, `Array( Lit(_)* )` is a valid pattern because the parameter of
 
 ## The IsMatch Trait
 
-The pattern syntax and the *PatternTree* are independant of specific syntax tree
+The pattern syntax and the *PatternTree* are independent of specific syntax tree
 implementations (rust ast / hir, syn, ...). When looking at the different
 pattern examples in the previous sections, it can be seen that the patterns
 don't contain any information specific to a certain syntax tree implementation.
@@ -717,7 +717,7 @@ if false {
 #### Problems
 
 Extending Rust syntax (which is quite complex by itself) with additional syntax
-needed for specifying patterns (alternations, sequences, repetisions, named
+needed for specifying patterns (alternations, sequences, repetitions, named
 submatches, ...) might become difficult to read and really hard to parse
 properly.
 
@@ -858,7 +858,7 @@ would be evaluated as soon as the `Block(_)#then` was matched.
 Another idea in this area would be to introduce a syntax for backreferences.
 They could be used to require that multiple parts of a pattern should match the
 same value. For example, the `assign_op_pattern` lint that searches for `a = a
-op b` and recommends changing it to `a op= b` requires that both occurrances of
+op b` and recommends changing it to `a op= b` requires that both occurrences of
 `a` are the same. Using `=#...` as syntax for backreferences, the lint could be
 implemented like this:
 
@@ -882,7 +882,7 @@ least two return statements" could be a practical addition.
 For patterns like "a literal that is not a boolean literal" one currently needs
 to list all alternatives except the boolean case. Introducing a negation
 operator that allows to write `Lit(!Bool(_))` might be a good idea. This pattern
-would be eqivalent to `Lit( Char(_) | Int(_) )` (given that currently only three
+would be equivalent to `Lit( Char(_) | Int(_) )` (given that currently only three
 literal types are implemented).
 
 #### Functional composition
diff --git a/src/tools/clippy/clippy_lints/Cargo.toml b/src/tools/clippy/clippy_lints/Cargo.toml
index 796f1ff1695..0b3846c1316 100644
--- a/src/tools/clippy/clippy_lints/Cargo.toml
+++ b/src/tools/clippy/clippy_lints/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "clippy_lints"
-version = "0.1.69"
+version = "0.1.70"
 description = "A bunch of helpful lints to avoid common pitfalls in Rust"
 repository = "https://github.com/rust-lang/rust-clippy"
 readme = "README.md"
diff --git a/src/tools/clippy/clippy_lints/src/almost_complete_range.rs b/src/tools/clippy/clippy_lints/src/almost_complete_range.rs
index 42e14b5cd94..32d80f42e7e 100644
--- a/src/tools/clippy/clippy_lints/src/almost_complete_range.rs
+++ b/src/tools/clippy/clippy_lints/src/almost_complete_range.rs
@@ -24,7 +24,7 @@ declare_clippy_lint! {
     /// ```rust
     /// let _ = 'a'..='z';
     /// ```
-    #[clippy::version = "1.63.0"]
+    #[clippy::version = "1.68.0"]
     pub ALMOST_COMPLETE_RANGE,
     suspicious,
     "almost complete range"
diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_slice_from_raw_parts.rs b/src/tools/clippy/clippy_lints/src/casts/cast_slice_from_raw_parts.rs
index 627b795d6ed..1233c632a79 100644
--- a/src/tools/clippy/clippy_lints/src/casts/cast_slice_from_raw_parts.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/cast_slice_from_raw_parts.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::msrvs::{self, Msrv};
-use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::source::snippet_with_context;
 use clippy_utils::{match_def_path, paths};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
@@ -34,6 +34,8 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>,
         if let ExprKind::Path(ref qpath) = fun.kind;
         if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id();
         if let Some(rpk) = raw_parts_kind(cx, fun_def_id);
+        let ctxt = expr.span.ctxt();
+        if cast_expr.span.ctxt() == ctxt;
         then {
             let func = match rpk {
                 RawPartsKind::Immutable => "from_raw_parts",
@@ -41,8 +43,8 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>,
             };
             let span = expr.span;
             let mut applicability = Applicability::MachineApplicable;
-            let ptr = snippet_with_applicability(cx, ptr_arg.span, "ptr", &mut applicability);
-            let len = snippet_with_applicability(cx, len_arg.span, "len", &mut applicability);
+            let ptr = snippet_with_context(cx, ptr_arg.span, ctxt, "ptr", &mut applicability).0;
+            let len = snippet_with_context(cx, len_arg.span, ctxt, "len", &mut applicability).0;
             span_lint_and_sugg(
                 cx,
                 CAST_SLICE_FROM_RAW_PARTS,
diff --git a/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs b/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs
new file mode 100644
index 00000000000..10f2bef268a
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs
@@ -0,0 +1,122 @@
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::visitors::for_each_expr_with_closures;
+use clippy_utils::{get_enclosing_block, get_parent_node, path_to_local_id};
+use core::ops::ControlFlow;
+use rustc_hir::{Block, ExprKind, HirId, Local, Node, PatKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::symbol::sym;
+use rustc_span::Symbol;
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for collections that are never queried.
+    ///
+    /// ### Why is this bad?
+    /// Putting effort into constructing a collection but then never querying it might indicate that
+    /// the author forgot to do whatever they intended to do with the collection. Example: Clone
+    /// a vector, sort it for iteration, but then mistakenly iterate the original vector
+    /// instead.
+    ///
+    /// ### Example
+    /// ```rust
+    /// # let samples = vec![3, 1, 2];
+    /// let mut sorted_samples = samples.clone();
+    /// sorted_samples.sort();
+    /// for sample in &samples { // Oops, meant to use `sorted_samples`.
+    ///     println!("{sample}");
+    /// }
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// # let samples = vec![3, 1, 2];
+    /// let mut sorted_samples = samples.clone();
+    /// sorted_samples.sort();
+    /// for sample in &sorted_samples {
+    ///     println!("{sample}");
+    /// }
+    /// ```
+    #[clippy::version = "1.69.0"]
+    pub COLLECTION_IS_NEVER_READ,
+    nursery,
+    "a collection is never queried"
+}
+declare_lint_pass!(CollectionIsNeverRead => [COLLECTION_IS_NEVER_READ]);
+
+static COLLECTIONS: [Symbol; 10] = [
+    sym::BTreeMap,
+    sym::BTreeSet,
+    sym::BinaryHeap,
+    sym::HashMap,
+    sym::HashSet,
+    sym::LinkedList,
+    sym::Option,
+    sym::String,
+    sym::Vec,
+    sym::VecDeque,
+];
+
+impl<'tcx> LateLintPass<'tcx> for CollectionIsNeverRead {
+    fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) {
+        // Look for local variables whose type is a container. Search surrounding bock for read access.
+        let ty = cx.typeck_results().pat_ty(local.pat);
+        if COLLECTIONS.iter().any(|&sym| is_type_diagnostic_item(cx, ty, sym))
+            && let PatKind::Binding(_, local_id, _, _) = local.pat.kind
+            && let Some(enclosing_block) = get_enclosing_block(cx, local.hir_id)
+            && has_no_read_access(cx, local_id, enclosing_block)
+        {
+            span_lint(cx, COLLECTION_IS_NEVER_READ, local.span, "collection is never read");
+        }
+    }
+}
+
+fn has_no_read_access<'tcx>(cx: &LateContext<'tcx>, id: HirId, block: &'tcx Block<'tcx>) -> bool {
+    let mut has_access = false;
+    let mut has_read_access = false;
+
+    // Inspect all expressions and sub-expressions in the block.
+    for_each_expr_with_closures(cx, block, |expr| {
+        // Ignore expressions that are not simply `id`.
+        if !path_to_local_id(expr, id) {
+            return ControlFlow::Continue(());
+        }
+
+        // `id` is being accessed. Investigate if it's a read access.
+        has_access = true;
+
+        // `id` appearing in the left-hand side of an assignment is not a read access:
+        //
+        // id = ...; // Not reading `id`.
+        if let Some(Node::Expr(parent)) = get_parent_node(cx.tcx, expr.hir_id)
+            && let ExprKind::Assign(lhs, ..) = parent.kind
+            && path_to_local_id(lhs, id)
+        {
+            return ControlFlow::Continue(());
+        }
+
+        // Method call on `id` in a statement ignores any return value, so it's not a read access:
+        //
+        // id.foo(...); // Not reading `id`.
+        //
+        // Only assuming this for "official" methods defined on the type. For methods defined in extension
+        // traits (identified as local, based on the orphan rule), pessimistically assume that they might
+        // have side effects, so consider them a read.
+        if let Some(Node::Expr(parent)) = get_parent_node(cx.tcx, expr.hir_id)
+            && let ExprKind::MethodCall(_, receiver, _, _) = parent.kind
+            && path_to_local_id(receiver, id)
+            && let Some(Node::Stmt(..)) = get_parent_node(cx.tcx, parent.hir_id)
+            && let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(parent.hir_id)
+            && !method_def_id.is_local()
+        {
+            return ControlFlow::Continue(());
+        }
+
+        // Any other access to `id` is a read access. Stop searching.
+        has_read_access = true;
+        ControlFlow::Break(())
+    });
+
+    // Ignore collections that have no access at all. Other lints should catch them.
+    has_access && !has_read_access
+}
diff --git a/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs
index cd5dd7a5706..cc6024b87cd 100644
--- a/src/tools/clippy/clippy_lints/src/declared_lints.rs
+++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs
@@ -92,6 +92,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::cognitive_complexity::COGNITIVE_COMPLEXITY_INFO,
     crate::collapsible_if::COLLAPSIBLE_ELSE_IF_INFO,
     crate::collapsible_if::COLLAPSIBLE_IF_INFO,
+    crate::collection_is_never_read::COLLECTION_IS_NEVER_READ_INFO,
     crate::comparison_chain::COMPARISON_CHAIN_INFO,
     crate::copies::BRANCHES_SHARING_CODE_INFO,
     crate::copies::IFS_SAME_COND_INFO,
@@ -226,6 +227,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::let_underscore::LET_UNDERSCORE_LOCK_INFO,
     crate::let_underscore::LET_UNDERSCORE_MUST_USE_INFO,
     crate::let_underscore::LET_UNDERSCORE_UNTYPED_INFO,
+    crate::let_with_type_underscore::LET_WITH_TYPE_UNDERSCORE_INFO,
     crate::lifetimes::EXTRA_UNUSED_LIFETIMES_INFO,
     crate::lifetimes::NEEDLESS_LIFETIMES_INFO,
     crate::literal_representation::DECIMAL_LITERAL_REPRESENTATION_INFO,
@@ -416,6 +418,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::misc_early::UNSEPARATED_LITERAL_SUFFIX_INFO,
     crate::misc_early::ZERO_PREFIXED_LITERAL_INFO,
     crate::mismatching_type_param_order::MISMATCHING_TYPE_PARAM_ORDER_INFO,
+    crate::missing_assert_message::MISSING_ASSERT_MESSAGE_INFO,
     crate::missing_const_for_fn::MISSING_CONST_FOR_FN_INFO,
     crate::missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS_INFO,
     crate::missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES_INFO,
@@ -517,6 +520,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::ranges::REVERSED_EMPTY_RANGES_INFO,
     crate::rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT_INFO,
     crate::read_zero_byte_vec::READ_ZERO_BYTE_VEC_INFO,
+    crate::redundant_async_block::REDUNDANT_ASYNC_BLOCK_INFO,
     crate::redundant_clone::REDUNDANT_CLONE_INFO,
     crate::redundant_closure_call::REDUNDANT_CLOSURE_CALL_INFO,
     crate::redundant_else::REDUNDANT_ELSE_INFO,
diff --git a/src/tools/clippy/clippy_lints/src/default_instead_of_iter_empty.rs b/src/tools/clippy/clippy_lints/src/default_instead_of_iter_empty.rs
index 1ad929864b2..f296b80d283 100644
--- a/src/tools/clippy/clippy_lints/src/default_instead_of_iter_empty.rs
+++ b/src/tools/clippy/clippy_lints/src/default_instead_of_iter_empty.rs
@@ -1,11 +1,12 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::last_path_segment;
-use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::source::snippet_with_context;
 use clippy_utils::{match_def_path, paths};
 use rustc_errors::Applicability;
 use rustc_hir::{def, Expr, ExprKind, GenericArg, QPath, TyKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::SyntaxContext;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -38,9 +39,11 @@ impl<'tcx> LateLintPass<'tcx> for DefaultIterEmpty {
             && let QPath::Resolved(None, path) = ty_path
             && let def::Res::Def(_, def_id) = &path.res
             && match_def_path(cx, *def_id, &paths::ITER_EMPTY)
+            && let ctxt = expr.span.ctxt()
+            && ty.span.ctxt() == ctxt
         {
             let mut applicability = Applicability::MachineApplicable;
-            let sugg = make_sugg(cx, ty_path, &mut applicability);
+            let sugg = make_sugg(cx, ty_path, ctxt, &mut applicability);
             span_lint_and_sugg(
                 cx,
                 DEFAULT_INSTEAD_OF_ITER_EMPTY,
@@ -54,14 +57,19 @@ impl<'tcx> LateLintPass<'tcx> for DefaultIterEmpty {
     }
 }
 
-fn make_sugg(cx: &LateContext<'_>, ty_path: &rustc_hir::QPath<'_>, applicability: &mut Applicability) -> String {
+fn make_sugg(
+    cx: &LateContext<'_>,
+    ty_path: &rustc_hir::QPath<'_>,
+    ctxt: SyntaxContext,
+    applicability: &mut Applicability,
+) -> String {
     if let Some(last) = last_path_segment(ty_path).args
         && let Some(iter_ty) = last.args.iter().find_map(|arg| match arg {
             GenericArg::Type(ty) => Some(ty),
             _ => None,
         })
     {
-        format!("std::iter::empty::<{}>()", snippet_with_applicability(cx, iter_ty.span, "..", applicability))
+        format!("std::iter::empty::<{}>()", snippet_with_context(cx, iter_ty.span, ctxt, "..", applicability).0)
     } else {
         "std::iter::empty()".to_owned()
     }
diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs
index 47501980e66..7f3f26bed7c 100644
--- a/src/tools/clippy/clippy_lints/src/dereference.rs
+++ b/src/tools/clippy/clippy_lints/src/dereference.rs
@@ -1357,10 +1357,10 @@ fn replace_types<'tcx>(
                     && let Some(term_ty) = projection_predicate.term.ty()
                     && let ty::Param(term_param_ty) = term_ty.kind()
                 {
-                    let item_def_id = projection_predicate.projection_ty.def_id;
-                    let assoc_item = cx.tcx.associated_item(item_def_id);
-                    let projection = cx.tcx
-                        .mk_projection(assoc_item.def_id, cx.tcx.mk_substs_trait(new_ty, []));
+                    let projection = cx.tcx.mk_ty_from_kind(ty::Alias(
+                        ty::Projection,
+                        projection_predicate.projection_ty.with_self_ty(cx.tcx, new_ty),
+                    ));
 
                     if let Ok(projected_ty) = cx.tcx.try_normalize_erasing_regions(cx.param_env, projection)
                         && substs[term_param_ty.index as usize] != ty::GenericArg::from(projected_ty)
diff --git a/src/tools/clippy/clippy_lints/src/derivable_impls.rs b/src/tools/clippy/clippy_lints/src/derivable_impls.rs
index c5f4e943f4f..8a5a28c6b3d 100644
--- a/src/tools/clippy/clippy_lints/src/derivable_impls.rs
+++ b/src/tools/clippy/clippy_lints/src/derivable_impls.rs
@@ -8,7 +8,7 @@ use rustc_hir::{
     Body, Expr, ExprKind, GenericArg, Impl, ImplItemKind, Item, ItemKind, Node, PathSegment, QPath, Ty, TyKind,
 };
 use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty::AdtDef;
+use rustc_middle::ty::{Adt, AdtDef, SubstsRef};
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::sym;
 
@@ -81,13 +81,18 @@ fn check_struct<'tcx>(
     self_ty: &Ty<'_>,
     func_expr: &Expr<'_>,
     adt_def: AdtDef<'_>,
+    substs: SubstsRef<'_>,
 ) {
     if let TyKind::Path(QPath::Resolved(_, p)) = self_ty.kind {
-        if let Some(PathSegment { args: Some(a), .. }) = p.segments.last() {
-            for arg in a.args {
-                if !matches!(arg, GenericArg::Lifetime(_)) {
-                    return;
-                }
+        if let Some(PathSegment { args, .. }) = p.segments.last() {
+            let args = args.map(|a| a.args).unwrap_or(&[]);
+
+            // substs contains the generic parameters of the type declaration, while args contains the arguments
+            // used at instantiation time. If both len are not equal, it means that some parameters were not
+            // provided (which means that the default values were used); in this case we will not risk
+            // suggesting too broad a rewrite. We won't either if any argument is a type or a const.
+            if substs.len() != args.len() || args.iter().any(|arg| !matches!(arg, GenericArg::Lifetime(_))) {
+                return;
             }
         }
     }
@@ -184,7 +189,7 @@ impl<'tcx> LateLintPass<'tcx> for DerivableImpls {
             if let Some(Node::ImplItem(impl_item)) = cx.tcx.hir().find(impl_item_hir);
             if let ImplItemKind::Fn(_, b) = &impl_item.kind;
             if let Body { value: func_expr, .. } = cx.tcx.hir().body(*b);
-            if let Some(adt_def) = cx.tcx.type_of(item.owner_id).subst_identity().ty_adt_def();
+            if let &Adt(adt_def, substs) = cx.tcx.type_of(item.owner_id).subst_identity().kind();
             if let attrs = cx.tcx.hir().attrs(item.hir_id());
             if !attrs.iter().any(|attr| attr.doc_str().is_some());
             if let child_attrs = cx.tcx.hir().attrs(impl_item_hir);
@@ -192,7 +197,7 @@ impl<'tcx> LateLintPass<'tcx> for DerivableImpls {
 
             then {
                 if adt_def.is_struct() {
-                    check_struct(cx, item, self_ty, func_expr, adt_def);
+                    check_struct(cx, item, self_ty, func_expr, adt_def, substs);
                 } else if adt_def.is_enum() && self.msrv.meets(msrvs::DEFAULT_ENUM_ATTRIBUTE) {
                     check_enum(cx, item, func_expr, adt_def);
                 }
diff --git a/src/tools/clippy/clippy_lints/src/exit.rs b/src/tools/clippy/clippy_lints/src/exit.rs
index 9c8b0d076df..8ba6a9e4876 100644
--- a/src/tools/clippy/clippy_lints/src/exit.rs
+++ b/src/tools/clippy/clippy_lints/src/exit.rs
@@ -11,7 +11,7 @@ declare_clippy_lint! {
     ///
     /// ### Why is this bad?
     /// Exit terminates the program at the location it is called. For unrecoverable
-    /// errors `panics` should be used to provide a stacktrace and potentualy other
+    /// errors `panics` should be used to provide a stacktrace and potentially other
     /// information. A normal termination or one with an error code should happen in
     /// the main function.
     ///
diff --git a/src/tools/clippy/clippy_lints/src/fn_null_check.rs b/src/tools/clippy/clippy_lints/src/fn_null_check.rs
index 91c8c340ce2..d8f4a5fe221 100644
--- a/src/tools/clippy/clippy_lints/src/fn_null_check.rs
+++ b/src/tools/clippy/clippy_lints/src/fn_null_check.rs
@@ -25,7 +25,7 @@ declare_clippy_lint! {
     ///
     /// if fn_ptr.is_none() { ... }
     /// ```
-    #[clippy::version = "1.67.0"]
+    #[clippy::version = "1.68.0"]
     pub FN_NULL_CHECK,
     correctness,
     "`fn()` type assumed to be nullable"
diff --git a/src/tools/clippy/clippy_lints/src/format.rs b/src/tools/clippy/clippy_lints/src/format.rs
index d0fab694960..8040938c626 100644
--- a/src/tools/clippy/clippy_lints/src/format.rs
+++ b/src/tools/clippy/clippy_lints/src/format.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::macros::{root_macro_call_first_node, FormatArgsExpn};
-use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::source::snippet_with_context;
 use clippy_utils::sugg::Sugg;
 use if_chain::if_chain;
 use rustc_errors::Applicability;
@@ -84,9 +84,9 @@ impl<'tcx> LateLintPass<'tcx> for UselessFormat {
                         _ => false,
                     };
                     let sugg = if is_new_string {
-                        snippet_with_applicability(cx, value.span, "..", &mut applicability).into_owned()
+                        snippet_with_context(cx, value.span, call_site.ctxt(), "..", &mut applicability).0.into_owned()
                     } else {
-                        let sugg = Sugg::hir_with_applicability(cx, value, "<arg>", &mut applicability);
+                        let sugg = Sugg::hir_with_context(cx, value, call_site.ctxt(), "<arg>", &mut applicability);
                         format!("{}.to_string()", sugg.maybe_par())
                     };
                     span_useless_format(cx, call_site, sugg, applicability);
diff --git a/src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs b/src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs
index 2811a73f6c1..d3d0d91c1be 100644
--- a/src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs
+++ b/src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs
@@ -22,7 +22,7 @@ pub(super) fn check_fn<'tcx>(cx: &LateContext<'_>, kind: &'tcx FnKind<'_>, body:
                             if let Some(gen_span) = generics.span_for_param_suggestion() {
                                 diag.span_suggestion_with_style(
                                     gen_span,
-                                    "add a type paremeter",
+                                    "add a type parameter",
                                     format!(", {{ /* Generic name */ }}: {}", &param.name.ident().as_str()[5..]),
                                     rustc_errors::Applicability::HasPlaceholders,
                                     rustc_errors::SuggestionStyle::ShowAlways,
@@ -35,7 +35,7 @@ pub(super) fn check_fn<'tcx>(cx: &LateContext<'_>, kind: &'tcx FnKind<'_>, body:
                                         ident.span.ctxt(),
                                         ident.span.parent(),
                                     ),
-                                    "add a type paremeter",
+                                    "add a type parameter",
                                     format!("<{{ /* Generic name */ }}: {}>", &param.name.ident().as_str()[5..]),
                                     rustc_errors::Applicability::HasPlaceholders,
                                     rustc_errors::SuggestionStyle::ShowAlways,
diff --git a/src/tools/clippy/clippy_lints/src/functions/misnamed_getters.rs b/src/tools/clippy/clippy_lints/src/functions/misnamed_getters.rs
index 8b53ee68ebd..e5945939e60 100644
--- a/src/tools/clippy/clippy_lints/src/functions/misnamed_getters.rs
+++ b/src/tools/clippy/clippy_lints/src/functions/misnamed_getters.rs
@@ -97,7 +97,7 @@ pub fn check_fn(cx: &LateContext<'_>, kind: FnKind<'_>, decl: &FnDecl<'_>, body:
 
     let Some(correct_field) = correct_field else {
         // There is no field corresponding to the getter name.
-        // FIXME: This can be a false positive if the correct field is reachable trought deeper autodereferences than used_field is
+        // FIXME: This can be a false positive if the correct field is reachable through deeper autodereferences than used_field is
         return;
     };
 
diff --git a/src/tools/clippy/clippy_lints/src/functions/mod.rs b/src/tools/clippy/clippy_lints/src/functions/mod.rs
index d2852b4acad..7c5e44bb7dc 100644
--- a/src/tools/clippy/clippy_lints/src/functions/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/functions/mod.rs
@@ -185,7 +185,7 @@ declare_clippy_lint! {
     /// ### Examples
     /// ```rust
     /// // this could be annotated with `#[must_use]`.
-    /// fn id<T>(t: T) -> T { t }
+    /// pub fn id<T>(t: T) -> T { t }
     /// ```
     #[clippy::version = "1.40.0"]
     pub MUST_USE_CANDIDATE,
diff --git a/src/tools/clippy/clippy_lints/src/implicit_saturating_add.rs b/src/tools/clippy/clippy_lints/src/implicit_saturating_add.rs
index 6e19343931e..57e6caa8711 100644
--- a/src/tools/clippy/clippy_lints/src/implicit_saturating_add.rs
+++ b/src/tools/clippy/clippy_lints/src/implicit_saturating_add.rs
@@ -1,7 +1,7 @@
 use clippy_utils::consts::{constant, Constant};
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::get_parent_expr;
-use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::source::snippet_with_context;
 use if_chain::if_chain;
 use rustc_ast::ast::{LitIntType, LitKind};
 use rustc_errors::Applicability;
@@ -55,6 +55,9 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingAdd {
             if let ExprKind::AssignOp(op1, target, value) = ex.kind;
             let ty = cx.typeck_results().expr_ty(target);
             if Some(c) == get_int_max(ty);
+            let ctxt = expr.span.ctxt();
+            if ex.span.ctxt() == ctxt;
+            if expr1.span.ctxt() == ctxt;
             if clippy_utils::SpanlessEq::new(cx).eq_expr(l, target);
             if BinOpKind::Add == op1.node;
             if let ExprKind::Lit(ref lit) = value.kind;
@@ -62,8 +65,15 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingAdd {
             if block.expr.is_none();
             then {
                 let mut app = Applicability::MachineApplicable;
-                let code = snippet_with_applicability(cx, target.span, "_", &mut app);
-                let sugg = if let Some(parent) = get_parent_expr(cx, expr) && let ExprKind::If(_cond, _then, Some(else_)) = parent.kind && else_.hir_id == expr.hir_id {format!("{{{code} = {code}.saturating_add(1); }}")} else {format!("{code} = {code}.saturating_add(1);")};
+                let code = snippet_with_context(cx, target.span, ctxt, "_", &mut app).0;
+                let sugg = if let Some(parent) = get_parent_expr(cx, expr)
+                    && let ExprKind::If(_cond, _then, Some(else_)) = parent.kind
+                    && else_.hir_id == expr.hir_id
+                {
+                    format!("{{{code} = {code}.saturating_add(1); }}")
+                } else {
+                    format!("{code} = {code}.saturating_add(1);")
+                };
                 span_lint_and_sugg(cx, IMPLICIT_SATURATING_ADD, expr.span, "manual saturating add detected", "use instead", sugg, app);
             }
         }
diff --git a/src/tools/clippy/clippy_lints/src/instant_subtraction.rs b/src/tools/clippy/clippy_lints/src/instant_subtraction.rs
index 668110c7cc0..34e9991582c 100644
--- a/src/tools/clippy/clippy_lints/src/instant_subtraction.rs
+++ b/src/tools/clippy/clippy_lints/src/instant_subtraction.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::{self, span_lint_and_sugg};
 use clippy_utils::msrvs::{self, Msrv};
-use clippy_utils::source;
+use clippy_utils::source::snippet_with_context;
 use clippy_utils::sugg::Sugg;
 use clippy_utils::ty;
 use rustc_errors::Applicability;
@@ -161,14 +161,9 @@ fn print_unchecked_duration_subtraction_sugg(
 ) {
     let mut applicability = Applicability::MachineApplicable;
 
-    let left_expr =
-        source::snippet_with_applicability(cx, left_expr.span, "std::time::Instant::now()", &mut applicability);
-    let right_expr = source::snippet_with_applicability(
-        cx,
-        right_expr.span,
-        "std::time::Duration::from_secs(1)",
-        &mut applicability,
-    );
+    let ctxt = expr.span.ctxt();
+    let left_expr = snippet_with_context(cx, left_expr.span, ctxt, "<instant>", &mut applicability).0;
+    let right_expr = snippet_with_context(cx, right_expr.span, ctxt, "<duration>", &mut applicability).0;
 
     diagnostics::span_lint_and_sugg(
         cx,
diff --git a/src/tools/clippy/clippy_lints/src/len_zero.rs b/src/tools/clippy/clippy_lints/src/len_zero.rs
index e13bc47973b..0805b4b1979 100644
--- a/src/tools/clippy/clippy_lints/src/len_zero.rs
+++ b/src/tools/clippy/clippy_lints/src/len_zero.rs
@@ -1,13 +1,14 @@
 use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
-use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::{get_item_name, get_parent_as_impl, is_lint_allowed, peel_ref_operators};
+use clippy_utils::source::snippet_with_context;
+use clippy_utils::{get_item_name, get_parent_as_impl, is_lint_allowed, peel_ref_operators, sugg::Sugg};
 use if_chain::if_chain;
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
 use rustc_hir::def_id::DefIdSet;
 use rustc_hir::{
-    def_id::DefId, AssocItemKind, BinOpKind, Expr, ExprKind, FnRetTy, ImplItem, ImplItemKind, ImplicitSelfKind, Item,
-    ItemKind, Mutability, Node, TraitItemRef, TyKind, UnOp,
+    def::Res, def_id::DefId, lang_items::LangItem, AssocItemKind, BinOpKind, Expr, ExprKind, FnRetTy, GenericArg,
+    GenericBound, ImplItem, ImplItemKind, ImplicitSelfKind, Item, ItemKind, Mutability, Node, PathSegment, PrimTy,
+    QPath, TraitItemRef, TyKind, TypeBindingKind,
 };
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::{self, AssocKind, FnSig, Ty};
@@ -16,7 +17,6 @@ use rustc_span::{
     source_map::{Span, Spanned, Symbol},
     symbol::sym,
 };
-use std::borrow::Cow;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -251,33 +251,98 @@ fn check_trait_items(cx: &LateContext<'_>, visited_trait: &Item<'_>, trait_items
 }
 
 #[derive(Debug, Clone, Copy)]
-enum LenOutput<'tcx> {
+enum LenOutput {
     Integral,
     Option(DefId),
-    Result(DefId, Ty<'tcx>),
+    Result(DefId),
 }
-fn parse_len_output<'tcx>(cx: &LateContext<'_>, sig: FnSig<'tcx>) -> Option<LenOutput<'tcx>> {
+
+fn extract_future_output<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<&'tcx PathSegment<'tcx>> {
+    if let ty::Alias(_, alias_ty) = ty.kind() &&
+        let Some(Node::Item(item)) = cx.tcx.hir().get_if_local(alias_ty.def_id) &&
+        let Item { kind: ItemKind::OpaqueTy(opaque), .. } = item &&
+        opaque.bounds.len() == 1 &&
+        let GenericBound::LangItemTrait(LangItem::Future, _, _, generic_args) = &opaque.bounds[0] &&
+        generic_args.bindings.len() == 1 &&
+        let TypeBindingKind::Equality {
+            term: rustc_hir::Term::Ty(rustc_hir::Ty {kind: TyKind::Path(QPath::Resolved(_, path)), .. }),
+        } = &generic_args.bindings[0].kind &&
+        path.segments.len() == 1 {
+            return Some(&path.segments[0]);
+        }
+
+    None
+}
+
+fn is_first_generic_integral<'tcx>(segment: &'tcx PathSegment<'tcx>) -> bool {
+    if let Some(generic_args) = segment.args {
+        if generic_args.args.is_empty() {
+            return false;
+        }
+        let arg = &generic_args.args[0];
+        if let GenericArg::Type(rustc_hir::Ty {
+            kind: TyKind::Path(QPath::Resolved(_, path)),
+            ..
+        }) = arg
+        {
+            let segments = &path.segments;
+            let segment = &segments[0];
+            let res = &segment.res;
+            if matches!(res, Res::PrimTy(PrimTy::Uint(_))) || matches!(res, Res::PrimTy(PrimTy::Int(_))) {
+                return true;
+            }
+        }
+    }
+
+    false
+}
+
+fn parse_len_output<'tcx>(cx: &LateContext<'tcx>, sig: FnSig<'tcx>) -> Option<LenOutput> {
+    if let Some(segment) = extract_future_output(cx, sig.output()) {
+        let res = segment.res;
+
+        if matches!(res, Res::PrimTy(PrimTy::Uint(_))) || matches!(res, Res::PrimTy(PrimTy::Int(_))) {
+            return Some(LenOutput::Integral);
+        }
+
+        if let Res::Def(_, def_id) = res {
+            if cx.tcx.is_diagnostic_item(sym::Option, def_id) && is_first_generic_integral(segment) {
+                return Some(LenOutput::Option(def_id));
+            } else if cx.tcx.is_diagnostic_item(sym::Result, def_id) && is_first_generic_integral(segment) {
+                return Some(LenOutput::Result(def_id));
+            }
+        }
+
+        return None;
+    }
+
     match *sig.output().kind() {
         ty::Int(_) | ty::Uint(_) => Some(LenOutput::Integral),
         ty::Adt(adt, subs) if cx.tcx.is_diagnostic_item(sym::Option, adt.did()) => {
             subs.type_at(0).is_integral().then(|| LenOutput::Option(adt.did()))
         },
-        ty::Adt(adt, subs) if cx.tcx.is_diagnostic_item(sym::Result, adt.did()) => subs
-            .type_at(0)
-            .is_integral()
-            .then(|| LenOutput::Result(adt.did(), subs.type_at(1))),
+        ty::Adt(adt, subs) if cx.tcx.is_diagnostic_item(sym::Result, adt.did()) => {
+            subs.type_at(0).is_integral().then(|| LenOutput::Result(adt.did()))
+        },
         _ => None,
     }
 }
 
-impl<'tcx> LenOutput<'tcx> {
-    fn matches_is_empty_output(self, ty: Ty<'tcx>) -> bool {
+impl LenOutput {
+    fn matches_is_empty_output<'tcx>(self, cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
+        if let Some(segment) = extract_future_output(cx, ty) {
+            return match (self, segment.res) {
+                (_, Res::PrimTy(PrimTy::Bool)) => true,
+                (Self::Option(_), Res::Def(_, def_id)) if cx.tcx.is_diagnostic_item(sym::Option, def_id) => true,
+                (Self::Result(_), Res::Def(_, def_id)) if cx.tcx.is_diagnostic_item(sym::Result, def_id) => true,
+                _ => false,
+            };
+        }
+
         match (self, ty.kind()) {
             (_, &ty::Bool) => true,
             (Self::Option(id), &ty::Adt(adt, subs)) if id == adt.did() => subs.type_at(0).is_bool(),
-            (Self::Result(id, err_ty), &ty::Adt(adt, subs)) if id == adt.did() => {
-                subs.type_at(0).is_bool() && subs.type_at(1) == err_ty
-            },
+            (Self::Result(id), &ty::Adt(adt, subs)) if id == adt.did() => subs.type_at(0).is_bool(),
             _ => false,
         }
     }
@@ -301,9 +366,14 @@ impl<'tcx> LenOutput<'tcx> {
 }
 
 /// Checks if the given signature matches the expectations for `is_empty`
-fn check_is_empty_sig<'tcx>(sig: FnSig<'tcx>, self_kind: ImplicitSelfKind, len_output: LenOutput<'tcx>) -> bool {
+fn check_is_empty_sig<'tcx>(
+    cx: &LateContext<'tcx>,
+    sig: FnSig<'tcx>,
+    self_kind: ImplicitSelfKind,
+    len_output: LenOutput,
+) -> bool {
     match &**sig.inputs_and_output {
-        [arg, res] if len_output.matches_is_empty_output(*res) => {
+        [arg, res] if len_output.matches_is_empty_output(cx, *res) => {
             matches!(
                 (arg.kind(), self_kind),
                 (ty::Ref(_, _, Mutability::Not), ImplicitSelfKind::ImmRef)
@@ -315,11 +385,11 @@ fn check_is_empty_sig<'tcx>(sig: FnSig<'tcx>, self_kind: ImplicitSelfKind, len_o
 }
 
 /// Checks if the given type has an `is_empty` method with the appropriate signature.
-fn check_for_is_empty<'tcx>(
-    cx: &LateContext<'tcx>,
+fn check_for_is_empty(
+    cx: &LateContext<'_>,
     span: Span,
     self_kind: ImplicitSelfKind,
-    output: LenOutput<'tcx>,
+    output: LenOutput,
     impl_ty: DefId,
     item_name: Symbol,
     item_kind: &str,
@@ -352,6 +422,7 @@ fn check_for_is_empty<'tcx>(
         Some(is_empty)
             if !(is_empty.fn_has_self_parameter
                 && check_is_empty_sig(
+                    cx,
                     cx.tcx.fn_sig(is_empty.def_id).subst_identity().skip_binder(),
                     self_kind,
                     output,
@@ -431,7 +502,7 @@ fn check_len(
                 &format!("using `{op}is_empty` is clearer and more explicit"),
                 format!(
                     "{op}{}.is_empty()",
-                    snippet_with_applicability(cx, receiver.span, "_", &mut applicability)
+                    snippet_with_context(cx, receiver.span, span.ctxt(), "_", &mut applicability).0,
                 ),
                 applicability,
             );
@@ -444,13 +515,7 @@ fn check_empty_expr(cx: &LateContext<'_>, span: Span, lit1: &Expr<'_>, lit2: &Ex
         let mut applicability = Applicability::MachineApplicable;
 
         let lit1 = peel_ref_operators(cx, lit1);
-        let mut lit_str = snippet_with_applicability(cx, lit1.span, "_", &mut applicability);
-
-        // Wrap the expression in parentheses if it's a deref expression. Otherwise operator precedence will
-        // cause the code to dereference boolean(won't compile).
-        if let ExprKind::Unary(UnOp::Deref, _) = lit1.kind {
-            lit_str = Cow::from(format!("({lit_str})"));
-        }
+        let lit_str = Sugg::hir_with_context(cx, lit1, span.ctxt(), "_", &mut applicability).maybe_par();
 
         span_lint_and_sugg(
             cx,
diff --git a/src/tools/clippy/clippy_lints/src/let_underscore.rs b/src/tools/clippy/clippy_lints/src/let_underscore.rs
index 7600777fab9..51b5de27de8 100644
--- a/src/tools/clippy/clippy_lints/src/let_underscore.rs
+++ b/src/tools/clippy/clippy_lints/src/let_underscore.rs
@@ -124,7 +124,7 @@ declare_clippy_lint! {
     /// ```
     #[clippy::version = "1.69.0"]
     pub LET_UNDERSCORE_UNTYPED,
-    pedantic,
+    restriction,
     "non-binding `let` without a type annotation"
 }
 
diff --git a/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs b/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs
new file mode 100644
index 00000000000..ba51973f2f9
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs
@@ -0,0 +1,45 @@
+use clippy_utils::diagnostics::span_lint_and_help;
+use rustc_hir::*;
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::lint::in_external_macro;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Detects when a variable is declared with an explicit type of `_`.
+    /// ### Why is this bad?
+    /// It adds noise, `: _` provides zero clarity or utility.
+    /// ### Example
+    /// ```rust,ignore
+    /// let my_number: _ = 1;
+    /// ```
+    /// Use instead:
+    /// ```rust,ignore
+    /// let my_number = 1;
+    /// ```
+    #[clippy::version = "1.69.0"]
+    pub LET_WITH_TYPE_UNDERSCORE,
+    complexity,
+    "unneeded underscore type (`_`) in a variable declaration"
+}
+declare_lint_pass!(UnderscoreTyped => [LET_WITH_TYPE_UNDERSCORE]);
+
+impl LateLintPass<'_> for UnderscoreTyped {
+    fn check_local<'tcx>(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) {
+        if_chain! {
+            if !in_external_macro(cx.tcx.sess, local.span);
+            if let Some(ty) = local.ty; // Ensure that it has a type defined
+            if let TyKind::Infer = &ty.kind; // that type is '_'
+            if local.span.ctxt() == ty.span.ctxt();
+            then {
+                span_lint_and_help(cx,
+                    LET_WITH_TYPE_UNDERSCORE,
+                    local.span,
+                    "variable declared with type underscore",
+                    Some(ty.span.with_lo(local.pat.span.hi())),
+                    "remove the explicit type `_` declaration"
+                )
+            }
+        };
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs
index c626e0bd998..491732be208 100644
--- a/src/tools/clippy/clippy_lints/src/lib.rs
+++ b/src/tools/clippy/clippy_lints/src/lib.rs
@@ -87,6 +87,7 @@ mod casts;
 mod checked_conversions;
 mod cognitive_complexity;
 mod collapsible_if;
+mod collection_is_never_read;
 mod comparison_chain;
 mod copies;
 mod copy_iterator;
@@ -166,6 +167,7 @@ mod large_stack_arrays;
 mod len_zero;
 mod let_if_seq;
 mod let_underscore;
+mod let_with_type_underscore;
 mod lifetimes;
 mod literal_representation;
 mod loops;
@@ -192,6 +194,7 @@ mod minmax;
 mod misc;
 mod misc_early;
 mod mismatching_type_param_order;
+mod missing_assert_message;
 mod missing_const_for_fn;
 mod missing_doc;
 mod missing_enforced_import_rename;
@@ -249,6 +252,7 @@ mod question_mark_used;
 mod ranges;
 mod rc_clone_in_vec_init;
 mod read_zero_byte_vec;
+mod redundant_async_block;
 mod redundant_clone;
 mod redundant_closure_call;
 mod redundant_else;
@@ -533,6 +537,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
                 .collect(),
         ))
     });
+    store.register_early_pass(|| Box::new(utils::format_args_collector::FormatArgsCollector));
     store.register_late_pass(|_| Box::new(utils::dump_hir::DumpHir));
     store.register_late_pass(|_| Box::new(utils::author::Author));
     let await_holding_invalid_types = conf.await_holding_invalid_types.clone();
@@ -924,6 +929,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         ))
     });
     store.register_late_pass(|_| Box::new(no_mangle_with_rust_abi::NoMangleWithRustAbi));
+    store.register_late_pass(|_| Box::new(collection_is_never_read::CollectionIsNeverRead));
+    store.register_late_pass(|_| Box::new(missing_assert_message::MissingAssertMessage));
+    store.register_early_pass(|| Box::new(redundant_async_block::RedundantAsyncBlock));
+    store.register_late_pass(|_| Box::new(let_with_type_underscore::UnderscoreTyped));
     // add lints here, do not remove this comment, it's used in `new_lint`
 }
 
diff --git a/src/tools/clippy/clippy_lints/src/manual_async_fn.rs b/src/tools/clippy/clippy_lints/src/manual_async_fn.rs
index 3778eb4c732..f97c6bcb5d1 100644
--- a/src/tools/clippy/clippy_lints/src/manual_async_fn.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_async_fn.rs
@@ -1,5 +1,4 @@
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::match_function_call_with_def_id;
 use clippy_utils::source::{position_before_rarrow, snippet_block, snippet_opt};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
@@ -175,16 +174,10 @@ fn captures_all_lifetimes(inputs: &[Ty<'_>], output_lifetimes: &[LifetimeName])
 fn desugared_async_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) -> Option<&'tcx Body<'tcx>> {
     if_chain! {
         if let Some(block_expr) = block.expr;
-        if let Some(args) = cx
-            .tcx
-            .lang_items()
-            .identity_future_fn()
-            .and_then(|def_id| match_function_call_with_def_id(cx, block_expr, def_id));
-        if args.len() == 1;
         if let Expr {
             kind: ExprKind::Closure(&Closure { body, .. }),
             ..
-        } = args[0];
+        } = block_expr;
         let closure_body = cx.tcx.hir().body(body);
         if closure_body.generator_kind == Some(GeneratorKind::Async(AsyncGeneratorKind::Block));
         then {
diff --git a/src/tools/clippy/clippy_lints/src/manual_bits.rs b/src/tools/clippy/clippy_lints/src/manual_bits.rs
index 462d73cf0b9..bc815dc4a26 100644
--- a/src/tools/clippy/clippy_lints/src/manual_bits.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_bits.rs
@@ -1,11 +1,12 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::get_parent_expr;
 use clippy_utils::msrvs::{self, Msrv};
-use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::source::snippet_with_context;
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
 use rustc_hir::{BinOpKind, Expr, ExprKind, GenericArg, QPath};
-use rustc_lint::{LateContext, LateLintPass};
+use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty::{self, Ty};
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::sym;
@@ -55,13 +56,17 @@ impl<'tcx> LateLintPass<'tcx> for ManualBits {
         if_chain! {
             if let ExprKind::Binary(bin_op, left_expr, right_expr) = expr.kind;
             if let BinOpKind::Mul = &bin_op.node;
+            if !in_external_macro(cx.sess(), expr.span);
+            let ctxt = expr.span.ctxt();
+            if left_expr.span.ctxt() == ctxt;
+            if right_expr.span.ctxt() == ctxt;
             if let Some((real_ty, resolved_ty, other_expr)) = get_one_size_of_ty(cx, left_expr, right_expr);
             if matches!(resolved_ty.kind(), ty::Int(_) | ty::Uint(_));
             if let ExprKind::Lit(lit) = &other_expr.kind;
             if let LitKind::Int(8, _) = lit.node;
             then {
                 let mut app = Applicability::MachineApplicable;
-                let ty_snip = snippet_with_applicability(cx, real_ty.span, "..", &mut app);
+                let ty_snip = snippet_with_context(cx, real_ty.span, ctxt, "..", &mut app).0;
                 let sugg = create_sugg(cx, expr, format!("{ty_snip}::BITS"));
 
                 span_lint_and_sugg(
diff --git a/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs b/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs
index 2fd32c009ea..31264261f5d 100644
--- a/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs
@@ -1,5 +1,5 @@
 use clippy_utils::msrvs::{self, Msrv};
-use clippy_utils::{diagnostics::span_lint_and_sugg, higher, in_constant, macros::root_macro_call, source::snippet};
+use clippy_utils::{diagnostics::span_lint_and_sugg, higher, in_constant, macros::root_macro_call, sugg::Sugg};
 use rustc_ast::ast::RangeLimits;
 use rustc_ast::LitKind::{Byte, Char};
 use rustc_errors::Applicability;
@@ -115,15 +115,8 @@ fn check_is_ascii(cx: &LateContext<'_>, span: Span, recv: &Expr<'_>, range: &Cha
         CharRange::Otherwise => None,
     } {
         let default_snip = "..";
-        // `snippet_with_applicability` may set applicability to `MaybeIncorrect` for
-        // macro span, so we check applicability manually by comparing `recv` is not default.
-        let recv = snippet(cx, recv.span, default_snip);
-
-        let applicability = if recv == default_snip {
-            Applicability::HasPlaceholders
-        } else {
-            Applicability::MachineApplicable
-        };
+        let mut app = Applicability::MachineApplicable;
+        let recv = Sugg::hir_with_context(cx, recv, span.ctxt(), default_snip, &mut app).maybe_par();
 
         span_lint_and_sugg(
             cx,
@@ -132,7 +125,7 @@ fn check_is_ascii(cx: &LateContext<'_>, span: Span, recv: &Expr<'_>, range: &Cha
             "manual check for common ascii range",
             "try",
             format!("{recv}.{sugg}()"),
-            applicability,
+            app,
         );
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs b/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs
index 38f41d077c1..aafee92713f 100644
--- a/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs
@@ -1,7 +1,7 @@
 use clippy_utils::consts::{constant_full_int, FullInt};
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::msrvs::{self, Msrv};
-use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::source::snippet_with_context;
 use clippy_utils::{in_constant, path_to_local};
 use rustc_errors::Applicability;
 use rustc_hir::{BinOpKind, Expr, ExprKind, Node, TyKind};
@@ -60,12 +60,16 @@ impl<'tcx> LateLintPass<'tcx> for ManualRemEuclid {
             return;
         }
 
+        // (x % c + c) % c
         if let ExprKind::Binary(op1, expr1, right) = expr.kind
             && op1.node == BinOpKind::Rem
+            && let ctxt = expr.span.ctxt()
+            && expr1.span.ctxt() == ctxt
             && let Some(const1) = check_for_unsigned_int_constant(cx, right)
             && let ExprKind::Binary(op2, left, right) = expr1.kind
             && op2.node == BinOpKind::Add
             && let Some((const2, expr2)) = check_for_either_unsigned_int_constant(cx, left, right)
+            && expr2.span.ctxt() == ctxt
             && let ExprKind::Binary(op3, expr3, right) = expr2.kind
             && op3.node == BinOpKind::Rem
             && let Some(const3) = check_for_unsigned_int_constant(cx, right)
@@ -86,7 +90,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualRemEuclid {
                 };
 
                 let mut app = Applicability::MachineApplicable;
-                let rem_of = snippet_with_applicability(cx, expr3.span, "_", &mut app);
+                let rem_of = snippet_with_context(cx, expr3.span, ctxt, "_", &mut app).0;
                 span_lint_and_sugg(
                     cx,
                     MANUAL_REM_EUCLID,
diff --git a/src/tools/clippy/clippy_lints/src/match_result_ok.rs b/src/tools/clippy/clippy_lints/src/match_result_ok.rs
index a020282d234..6ec9784038c 100644
--- a/src/tools/clippy/clippy_lints/src/match_result_ok.rs
+++ b/src/tools/clippy/clippy_lints/src/match_result_ok.rs
@@ -1,11 +1,11 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::higher;
-use clippy_utils::method_chain_args;
-use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::is_res_lang_ctor;
+use clippy_utils::source::snippet_with_context;
 use clippy_utils::ty::is_type_diagnostic_item;
 use if_chain::if_chain;
 use rustc_errors::Applicability;
-use rustc_hir::{Expr, ExprKind, PatKind, QPath};
+use rustc_hir::{Expr, ExprKind, LangItem, PatKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::sym;
@@ -58,17 +58,18 @@ impl<'tcx> LateLintPass<'tcx> for MatchResultOk {
             };
 
         if_chain! {
-            if let ExprKind::MethodCall(ok_path, result_types_0, ..) = let_expr.kind; //check is expr.ok() has type Result<T,E>.ok(, _)
-            if let PatKind::TupleStruct(QPath::Resolved(_, x), y, _)  = let_pat.kind; //get operation
-            if method_chain_args(let_expr, &["ok"]).is_some(); //test to see if using ok() method use std::marker::Sized;
-            if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(result_types_0), sym::Result);
-            if rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_path(x, false)) == "Some";
-
+            if let ExprKind::MethodCall(ok_path, recv, [], ..) = let_expr.kind; //check is expr.ok() has type Result<T,E>.ok(, _)
+            if let PatKind::TupleStruct(ref pat_path, [ok_pat], _)  = let_pat.kind; //get operation
+            if ok_path.ident.as_str() == "ok";
+            if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result);
+            if is_res_lang_ctor(cx, cx.qpath_res(pat_path, let_pat.hir_id), LangItem::OptionSome);
+            let ctxt = expr.span.ctxt();
+            if let_expr.span.ctxt() == ctxt;
+            if let_pat.span.ctxt() == ctxt;
             then {
-
                 let mut applicability = Applicability::MachineApplicable;
-                let some_expr_string = snippet_with_applicability(cx, y[0].span, "", &mut applicability);
-                let trimmed_ok = snippet_with_applicability(cx, let_expr.span.until(ok_path.ident.span), "", &mut applicability);
+                let some_expr_string = snippet_with_context(cx, ok_pat.span, ctxt, "", &mut applicability).0;
+                let trimmed_ok = snippet_with_context(cx, recv.span, ctxt, "", &mut applicability).0;
                 let sugg = format!(
                     "{ifwhile} let Ok({some_expr_string}) = {}",
                     trimmed_ok.trim().trim_end_matches('.'),
diff --git a/src/tools/clippy/clippy_lints/src/matches/mod.rs b/src/tools/clippy/clippy_lints/src/matches/mod.rs
index 7b15a307fec..97ecca450fa 100644
--- a/src/tools/clippy/clippy_lints/src/matches/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/mod.rs
@@ -925,7 +925,7 @@ declare_clippy_lint! {
     #[clippy::version = "1.66.0"]
     pub MANUAL_FILTER,
     complexity,
-    "reimplentation of `filter`"
+    "reimplementation of `filter`"
 }
 
 #[derive(Default)]
diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs
index 702df4b282b..56e3988bf09 100644
--- a/src/tools/clippy/clippy_lints/src/methods/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs
@@ -340,8 +340,9 @@ declare_clippy_lint! {
 
 declare_clippy_lint! {
     /// ### What it does
-    /// Checks for methods with certain name prefixes and which
-    /// doesn't match how self is taken. The actual rules are:
+    /// Checks for methods with certain name prefixes or suffixes, and which
+    /// do not adhere to standard conventions regarding how `self` is taken.
+    /// The actual rules are:
     ///
     /// |Prefix |Postfix     |`self` taken                   | `self` type  |
     /// |-------|------------|-------------------------------|--------------|
diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs
index df26b36b7b3..4c4c003ca46 100644
--- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs
@@ -369,10 +369,10 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty<
             Node::Item(item) => {
                 if let ItemKind::Fn(_, _, body_id) = &item.kind
                 && let output_ty = return_ty(cx, item.owner_id)
-                && Inherited::build(cx.tcx, item.owner_id.def_id).enter(|inherited| {
-                    let fn_ctxt = FnCtxt::new(inherited, cx.param_env, item.owner_id.def_id);
-                    fn_ctxt.can_coerce(ty, output_ty)
-                }) {
+                && let inherited = Inherited::new(cx.tcx, item.owner_id.def_id)
+                && let fn_ctxt = FnCtxt::new(&inherited, cx.param_env, item.owner_id.def_id)
+                && fn_ctxt.can_coerce(ty, output_ty)
+                {
                     if has_lifetime(output_ty) && has_lifetime(ty) {
                         return false;
                     }
diff --git a/src/tools/clippy/clippy_lints/src/missing_assert_message.rs b/src/tools/clippy/clippy_lints/src/missing_assert_message.rs
new file mode 100644
index 00000000000..2214a568d9c
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/missing_assert_message.rs
@@ -0,0 +1,82 @@
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::macros::{find_assert_args, find_assert_eq_args, root_macro_call_first_node, PanicExpn};
+use clippy_utils::{is_in_cfg_test, is_in_test_function};
+use rustc_hir::Expr;
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::sym;
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks assertions without a custom panic message.
+    ///
+    /// ### Why is this bad?
+    /// Without a good custom message, it'd be hard to understand what went wrong when the assertion fails.
+    /// A good custom message should be more about why the failure of the assertion is problematic
+    /// and not what is failed because the assertion already conveys that.
+    ///
+    /// ### Known problems
+    /// This lint cannot check the quality of the custom panic messages.
+    /// Hence, you can suppress this lint simply by adding placeholder messages
+    /// like "assertion failed". However, we recommend coming up with good messages
+    /// that provide useful information instead of placeholder messages that
+    /// don't provide any extra information.
+    ///
+    /// ### Example
+    /// ```rust
+    /// # struct Service { ready: bool }
+    /// fn call(service: Service) {
+    ///     assert!(service.ready);
+    /// }
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// # struct Service { ready: bool }
+    /// fn call(service: Service) {
+    ///     assert!(service.ready, "`service.poll_ready()` must be called first to ensure that service is ready to receive requests");
+    /// }
+    /// ```
+    #[clippy::version = "1.69.0"]
+    pub MISSING_ASSERT_MESSAGE,
+    restriction,
+    "checks assertions without a custom panic message"
+}
+
+declare_lint_pass!(MissingAssertMessage => [MISSING_ASSERT_MESSAGE]);
+
+impl<'tcx> LateLintPass<'tcx> for MissingAssertMessage {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+        let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return };
+        let single_argument = match cx.tcx.get_diagnostic_name(macro_call.def_id) {
+            Some(sym::assert_macro | sym::debug_assert_macro) => true,
+            Some(
+                sym::assert_eq_macro | sym::assert_ne_macro | sym::debug_assert_eq_macro | sym::debug_assert_ne_macro,
+            ) => false,
+            _ => return,
+        };
+
+        // This lint would be very noisy in tests, so just ignore if we're in test context
+        if is_in_test_function(cx.tcx, expr.hir_id) || is_in_cfg_test(cx.tcx, expr.hir_id) {
+            return;
+        }
+
+        let panic_expn = if single_argument {
+            let Some((_, panic_expn)) = find_assert_args(cx, expr, macro_call.expn) else { return };
+            panic_expn
+        } else {
+            let Some((_, _, panic_expn)) = find_assert_eq_args(cx, expr, macro_call.expn) else { return };
+            panic_expn
+        };
+
+        if let PanicExpn::Empty = panic_expn {
+            span_lint_and_help(
+                cx,
+                MISSING_ASSERT_MESSAGE,
+                macro_call.span,
+                "assert without any message",
+                None,
+                "consider describing why the failing assert is problematic",
+            );
+        }
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/missing_doc.rs b/src/tools/clippy/clippy_lints/src/missing_doc.rs
index 5b1f03fc16c..f2773cad400 100644
--- a/src/tools/clippy/clippy_lints/src/missing_doc.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_doc.rs
@@ -8,10 +8,10 @@
 use clippy_utils::attrs::is_doc_hidden;
 use clippy_utils::diagnostics::span_lint;
 use clippy_utils::is_from_proc_macro;
-use hir::def_id::LocalDefId;
 use if_chain::if_chain;
 use rustc_ast::ast::{self, MetaItem, MetaItemKind};
 use rustc_hir as hir;
+use rustc_hir::def_id::LocalDefId;
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::ty::Visibility;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
@@ -21,8 +21,7 @@ use rustc_span::sym;
 
 declare_clippy_lint! {
     /// ### What it does
-    /// Warns if there is missing doc for any documentable item
-    /// (public or private).
+    /// Warns if there is missing doc for any private documentable item
     ///
     /// ### Why is this bad?
     /// Doc is good. *rustc* has a `MISSING_DOCS`
@@ -32,7 +31,7 @@ declare_clippy_lint! {
     #[clippy::version = "pre 1.29.0"]
     pub MISSING_DOCS_IN_PRIVATE_ITEMS,
     restriction,
-    "detects missing documentation for public and private members"
+    "detects missing documentation for private members"
 }
 
 pub struct MissingDoc {
@@ -107,11 +106,14 @@ impl MissingDoc {
             if vis == Visibility::Public || vis != Visibility::Restricted(CRATE_DEF_ID.into()) {
                 return;
             }
+        } else if def_id != CRATE_DEF_ID && cx.effective_visibilities.is_exported(def_id) {
+            return;
         }
 
         let has_doc = attrs
             .iter()
             .any(|a| a.doc_str().is_some() || Self::has_include(a.meta()));
+
         if !has_doc {
             span_lint(
                 cx,
diff --git a/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs b/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs
index 63c575fca30..5418616ded0 100644
--- a/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs
+++ b/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs
@@ -11,6 +11,7 @@ use rustc_ast::Mutability;
 use rustc_hir as hir;
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::lint::in_external_macro;
+use rustc_middle::ty;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::Span;
 
@@ -120,33 +121,15 @@ fn collect_unsafe_exprs<'tcx>(
                 unsafe_ops.push(("raw pointer dereference occurs here", expr.span));
             },
 
-            ExprKind::Call(path_expr, _) => match path_expr.kind {
-                ExprKind::Path(QPath::Resolved(
-                    _,
-                    hir::Path {
-                        res: Res::Def(kind, def_id),
-                        ..
-                    },
-                )) if kind.is_fn_like() => {
-                    let sig = cx.tcx.fn_sig(*def_id);
-                    if sig.0.unsafety() == Unsafety::Unsafe {
-                        unsafe_ops.push(("unsafe function call occurs here", expr.span));
-                    }
-                },
-
-                ExprKind::Path(QPath::TypeRelative(..)) => {
-                    if let Some(sig) = cx
-                        .typeck_results()
-                        .type_dependent_def_id(path_expr.hir_id)
-                        .map(|def_id| cx.tcx.fn_sig(def_id))
-                    {
-                        if sig.0.unsafety() == Unsafety::Unsafe {
-                            unsafe_ops.push(("unsafe function call occurs here", expr.span));
-                        }
-                    }
-                },
-
-                _ => {},
+            ExprKind::Call(path_expr, _) => {
+                let sig = match *cx.typeck_results().expr_ty(path_expr).kind() {
+                    ty::FnDef(id, _) => cx.tcx.fn_sig(id).skip_binder(),
+                    ty::FnPtr(sig) => sig,
+                    _ => return Continue(Descend::Yes),
+                };
+                if sig.unsafety() == Unsafety::Unsafe {
+                    unsafe_ops.push(("unsafe function call occurs here", expr.span));
+                }
             },
 
             ExprKind::MethodCall(..) => {
diff --git a/src/tools/clippy/clippy_lints/src/neg_multiply.rs b/src/tools/clippy/clippy_lints/src/neg_multiply.rs
index fb9a4abd0b4..ed3e2c6e7f4 100644
--- a/src/tools/clippy/clippy_lints/src/neg_multiply.rs
+++ b/src/tools/clippy/clippy_lints/src/neg_multiply.rs
@@ -1,6 +1,6 @@
 use clippy_utils::consts::{self, Constant};
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::source::snippet_with_context;
 use clippy_utils::sugg::has_enclosing_paren;
 use if_chain::if_chain;
 use rustc_ast::util::parser::PREC_PREFIX;
@@ -60,8 +60,8 @@ fn check_mul(cx: &LateContext<'_>, span: Span, lit: &Expr<'_>, exp: &Expr<'_>) {
 
         then {
             let mut applicability = Applicability::MachineApplicable;
-            let snip = snippet_with_applicability(cx, exp.span, "..", &mut applicability);
-            let suggestion = if exp.precedence().order() < PREC_PREFIX && !has_enclosing_paren(&snip) {
+            let (snip, from_macro) = snippet_with_context(cx, exp.span, span.ctxt(), "..", &mut applicability);
+            let suggestion = if !from_macro && exp.precedence().order() < PREC_PREFIX && !has_enclosing_paren(&snip) {
                 format!("-({snip})")
             } else {
                 format!("-{snip}")
diff --git a/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs b/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs
index 2ecb0487484..e1de494eb41 100644
--- a/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs
+++ b/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs
@@ -53,6 +53,7 @@ impl<'tcx> LateLintPass<'tcx> for NonOctalUnixPermissions {
                             || is_type_diagnostic_item(cx, obj_ty, sym::DirBuilder)))
                         || (path.ident.name == sym!(set_mode) && match_type(cx, obj_ty, &paths::PERMISSIONS));
                     if let ExprKind::Lit(_) = param.kind;
+                    if param.span.ctxt() == expr.span.ctxt();
 
                     then {
                         let Some(snip) = snippet_opt(cx, param.span) else {
@@ -71,6 +72,7 @@ impl<'tcx> LateLintPass<'tcx> for NonOctalUnixPermissions {
                     if let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id();
                     if match_def_path(cx, def_id, &paths::PERMISSIONS_FROM_MODE);
                     if let ExprKind::Lit(_) = param.kind;
+                    if param.span.ctxt() == expr.span.ctxt();
                     if let Some(snip) = snippet_opt(cx, param.span);
                     if !snip.starts_with("0o");
                     then {
diff --git a/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs b/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs
index 87a8a2ed12b..25e8de94863 100644
--- a/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs
+++ b/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs
@@ -143,6 +143,10 @@ impl ArithmeticSideEffects {
             return;
         }
         let has_valid_op = if Self::is_integral(lhs_ty) && Self::is_integral(rhs_ty) {
+            if let hir::BinOpKind::Shl | hir::BinOpKind::Shr = op.node {
+                // At least for integers, shifts are already handled by the CTFE
+                return;
+            }
             let (actual_lhs, lhs_ref_counter) = peel_hir_expr_refs(lhs);
             let (actual_rhs, rhs_ref_counter) = peel_hir_expr_refs(rhs);
             match (
@@ -151,10 +155,13 @@ impl ArithmeticSideEffects {
             ) {
                 (None, None) => false,
                 (None, Some(n)) | (Some(n), None) => match (&op.node, n) {
-                    (hir::BinOpKind::Div | hir::BinOpKind::Rem, 0) => false,
+                    // Division and module are always valid if applied to non-zero integers
+                    (hir::BinOpKind::Div | hir::BinOpKind::Rem, local_n) if local_n != 0 => true,
+                    // Addition or subtracting zeros is always a no-op
                     (hir::BinOpKind::Add | hir::BinOpKind::Sub, 0)
-                    | (hir::BinOpKind::Div | hir::BinOpKind::Rem, _)
-                    | (hir::BinOpKind::Mul, 0 | 1) => true,
+                    // Multiplication by 1 or 0 will never overflow
+                    | (hir::BinOpKind::Mul, 0 | 1)
+                    => true,
                     _ => false,
                 },
                 (Some(_), Some(_)) => {
diff --git a/src/tools/clippy/clippy_lints/src/permissions_set_readonly_false.rs b/src/tools/clippy/clippy_lints/src/permissions_set_readonly_false.rs
index e7095ec191f..664d44d6504 100644
--- a/src/tools/clippy/clippy_lints/src/permissions_set_readonly_false.rs
+++ b/src/tools/clippy/clippy_lints/src/permissions_set_readonly_false.rs
@@ -21,7 +21,7 @@ declare_clippy_lint! {
     /// let mut permissions = metadata.permissions();
     /// permissions.set_readonly(false);
     /// ```
-    #[clippy::version = "1.66.0"]
+    #[clippy::version = "1.68.0"]
     pub PERMISSIONS_SET_READONLY_FALSE,
     suspicious,
     "Checks for calls to `std::fs::Permissions.set_readonly` with argument `false`"
diff --git a/src/tools/clippy/clippy_lints/src/redundant_async_block.rs b/src/tools/clippy/clippy_lints/src/redundant_async_block.rs
new file mode 100644
index 00000000000..27ad4308637
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/redundant_async_block.rs
@@ -0,0 +1,84 @@
+use clippy_utils::{diagnostics::span_lint_and_sugg, source::snippet};
+use rustc_ast::ast::*;
+use rustc_ast::visit::Visitor as AstVisitor;
+use rustc_errors::Applicability;
+use rustc_lint::{EarlyContext, EarlyLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for `async` block that only returns `await` on a future.
+    ///
+    /// ### Why is this bad?
+    /// It is simpler and more efficient to use the future directly.
+    ///
+    /// ### Example
+    /// ```rust
+    /// async fn f() -> i32 {
+    ///     1 + 2
+    /// }
+    ///
+    /// let fut = async {
+    ///     f().await
+    /// };
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// async fn f() -> i32 {
+    ///     1 + 2
+    /// }
+    ///
+    /// let fut = f();
+    /// ```
+    #[clippy::version = "1.69.0"]
+    pub REDUNDANT_ASYNC_BLOCK,
+    complexity,
+    "`async { future.await }` can be replaced by `future`"
+}
+declare_lint_pass!(RedundantAsyncBlock => [REDUNDANT_ASYNC_BLOCK]);
+
+impl EarlyLintPass for RedundantAsyncBlock {
+    fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
+        if expr.span.from_expansion() {
+            return;
+        }
+        if let ExprKind::Async(_, _, block) = &expr.kind && block.stmts.len() == 1 &&
+            let Some(Stmt { kind: StmtKind::Expr(last), .. }) = block.stmts.last() &&
+            let ExprKind::Await(future) = &last.kind &&
+            !future.span.from_expansion() &&
+            !await_in_expr(future)
+        {
+            span_lint_and_sugg(
+                cx,
+                REDUNDANT_ASYNC_BLOCK,
+                expr.span,
+                "this async expression only awaits a single future",
+                "you can reduce it to",
+                snippet(cx, future.span, "..").into_owned(),
+                Applicability::MachineApplicable,
+            );
+        }
+    }
+}
+
+/// Check whether an expression contains `.await`
+fn await_in_expr(expr: &Expr) -> bool {
+    let mut detector = AwaitDetector::default();
+    detector.visit_expr(expr);
+    detector.await_found
+}
+
+#[derive(Default)]
+struct AwaitDetector {
+    await_found: bool,
+}
+
+impl<'ast> AstVisitor<'ast> for AwaitDetector {
+    fn visit_expr(&mut self, ex: &'ast Expr) {
+        match (&ex.kind, self.await_found) {
+            (ExprKind::Await(_), _) => self.await_found = true,
+            (_, false) => rustc_ast::visit::walk_expr(self, ex),
+            _ => (),
+        }
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/size_of_ref.rs b/src/tools/clippy/clippy_lints/src/size_of_ref.rs
index 3fcdb4288ce..8abec06c641 100644
--- a/src/tools/clippy/clippy_lints/src/size_of_ref.rs
+++ b/src/tools/clippy/clippy_lints/src/size_of_ref.rs
@@ -45,7 +45,7 @@ declare_clippy_lint! {
     ///     }
     /// }
     /// ```
-    #[clippy::version = "1.67.0"]
+    #[clippy::version = "1.68.0"]
     pub SIZE_OF_REF,
     suspicious,
     "Argument to `std::mem::size_of_val()` is a double-reference, which is almost certainly unintended"
diff --git a/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs b/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs
index e111c7d2291..8aa47b62ebf 100644
--- a/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs
+++ b/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs
@@ -596,8 +596,7 @@ fn ident_difference_expr_with_base_location(
         | (MethodCall(_), MethodCall(_))
         | (Call(_, _), Call(_, _))
         | (ConstBlock(_), ConstBlock(_))
-        | (Array(_), Array(_))
-        | (Box(_), Box(_)) => {
+        | (Array(_), Array(_)) => {
             // keep going
         },
         _ => {
diff --git a/src/tools/clippy/clippy_lints/src/swap.rs b/src/tools/clippy/clippy_lints/src/swap.rs
index 0f062cecf88..1aeac724ab1 100644
--- a/src/tools/clippy/clippy_lints/src/swap.rs
+++ b/src/tools/clippy/clippy_lints/src/swap.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
-use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::source::snippet_with_context;
 use clippy_utils::sugg::Sugg;
 use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::{can_mut_borrow_both, eq_expr_value, in_constant, std_or_core};
@@ -10,6 +10,7 @@ use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::source_map::Spanned;
+use rustc_span::SyntaxContext;
 use rustc_span::{sym, symbol::Ident, Span};
 
 declare_clippy_lint! {
@@ -80,43 +81,45 @@ impl<'tcx> LateLintPass<'tcx> for Swap {
 }
 
 fn generate_swap_warning(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>, span: Span, is_xor_based: bool) {
+    let ctxt = span.ctxt();
     let mut applicability = Applicability::MachineApplicable;
 
     if !can_mut_borrow_both(cx, e1, e2) {
-        if let ExprKind::Index(lhs1, idx1) = e1.kind {
-            if let ExprKind::Index(lhs2, idx2) = e2.kind {
-                if eq_expr_value(cx, lhs1, lhs2) {
-                    let ty = cx.typeck_results().expr_ty(lhs1).peel_refs();
+        if let ExprKind::Index(lhs1, idx1) = e1.kind
+            && let ExprKind::Index(lhs2, idx2) = e2.kind
+            && eq_expr_value(cx, lhs1, lhs2)
+            && e1.span.ctxt() == ctxt
+            && e2.span.ctxt() == ctxt
+        {
+            let ty = cx.typeck_results().expr_ty(lhs1).peel_refs();
 
-                    if matches!(ty.kind(), ty::Slice(_))
-                        || matches!(ty.kind(), ty::Array(_, _))
-                        || is_type_diagnostic_item(cx, ty, sym::Vec)
-                        || is_type_diagnostic_item(cx, ty, sym::VecDeque)
-                    {
-                        let slice = Sugg::hir_with_applicability(cx, lhs1, "<slice>", &mut applicability);
-                        span_lint_and_sugg(
-                            cx,
-                            MANUAL_SWAP,
-                            span,
-                            &format!("this looks like you are swapping elements of `{slice}` manually"),
-                            "try",
-                            format!(
-                                "{}.swap({}, {})",
-                                slice.maybe_par(),
-                                snippet_with_applicability(cx, idx1.span, "..", &mut applicability),
-                                snippet_with_applicability(cx, idx2.span, "..", &mut applicability),
-                            ),
-                            applicability,
-                        );
-                    }
-                }
+            if matches!(ty.kind(), ty::Slice(_))
+                || matches!(ty.kind(), ty::Array(_, _))
+                || is_type_diagnostic_item(cx, ty, sym::Vec)
+                || is_type_diagnostic_item(cx, ty, sym::VecDeque)
+            {
+                let slice = Sugg::hir_with_applicability(cx, lhs1, "<slice>", &mut applicability);
+                span_lint_and_sugg(
+                    cx,
+                    MANUAL_SWAP,
+                    span,
+                    &format!("this looks like you are swapping elements of `{slice}` manually"),
+                    "try",
+                    format!(
+                        "{}.swap({}, {});",
+                        slice.maybe_par(),
+                        snippet_with_context(cx, idx1.span, ctxt, "..", &mut applicability).0,
+                        snippet_with_context(cx, idx2.span, ctxt, "..", &mut applicability).0,
+                    ),
+                    applicability,
+                );
             }
         }
         return;
     }
 
-    let first = Sugg::hir_with_applicability(cx, e1, "..", &mut applicability);
-    let second = Sugg::hir_with_applicability(cx, e2, "..", &mut applicability);
+    let first = Sugg::hir_with_context(cx, e1, ctxt, "..", &mut applicability);
+    let second = Sugg::hir_with_context(cx, e2, ctxt, "..", &mut applicability);
     let Some(sugg) = std_or_core(cx) else { return };
 
     span_lint_and_then(
@@ -128,7 +131,7 @@ fn generate_swap_warning(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>, spa
             diag.span_suggestion(
                 span,
                 "try",
-                format!("{sugg}::mem::swap({}, {})", first.mut_addr(), second.mut_addr()),
+                format!("{sugg}::mem::swap({}, {});", first.mut_addr(), second.mut_addr()),
                 applicability,
             );
             if !is_xor_based {
@@ -144,19 +147,19 @@ fn check_manual_swap(cx: &LateContext<'_>, block: &Block<'_>) {
         return;
     }
 
-    for w in block.stmts.windows(3) {
+    for [s1, s2, s3] in block.stmts.array_windows::<3>() {
         if_chain! {
             // let t = foo();
-            if let StmtKind::Local(tmp) = w[0].kind;
+            if let StmtKind::Local(tmp) = s1.kind;
             if let Some(tmp_init) = tmp.init;
             if let PatKind::Binding(.., ident, None) = tmp.pat.kind;
 
             // foo() = bar();
-            if let StmtKind::Semi(first) = w[1].kind;
+            if let StmtKind::Semi(first) = s2.kind;
             if let ExprKind::Assign(lhs1, rhs1, _) = first.kind;
 
             // bar() = t;
-            if let StmtKind::Semi(second) = w[2].kind;
+            if let StmtKind::Semi(second) = s3.kind;
             if let ExprKind::Assign(lhs2, rhs2, _) = second.kind;
             if let ExprKind::Path(QPath::Resolved(None, rhs2)) = rhs2.kind;
             if rhs2.segments.len() == 1;
@@ -164,8 +167,15 @@ fn check_manual_swap(cx: &LateContext<'_>, block: &Block<'_>) {
             if ident.name == rhs2.segments[0].ident.name;
             if eq_expr_value(cx, tmp_init, lhs1);
             if eq_expr_value(cx, rhs1, lhs2);
+
+            let ctxt = s1.span.ctxt();
+            if s2.span.ctxt() == ctxt;
+            if s3.span.ctxt() == ctxt;
+            if first.span.ctxt() == ctxt;
+            if second.span.ctxt() == ctxt;
+
             then {
-                let span = w[0].span.to(second.span);
+                let span = s1.span.to(s3.span);
                 generate_swap_warning(cx, lhs1, lhs2, span, false);
             }
         }
@@ -246,17 +256,20 @@ fn parse<'a, 'hir>(stmt: &'a Stmt<'hir>) -> Option<(ExprOrIdent<'hir>, &'a Expr<
 
 /// Implementation of the xor case for `MANUAL_SWAP` lint.
 fn check_xor_swap(cx: &LateContext<'_>, block: &Block<'_>) {
-    for window in block.stmts.windows(3) {
+    for [s1, s2, s3] in block.stmts.array_windows::<3>() {
         if_chain! {
-            if let Some((lhs0, rhs0)) = extract_sides_of_xor_assign(&window[0]);
-            if let Some((lhs1, rhs1)) = extract_sides_of_xor_assign(&window[1]);
-            if let Some((lhs2, rhs2)) = extract_sides_of_xor_assign(&window[2]);
+            let ctxt = s1.span.ctxt();
+            if let Some((lhs0, rhs0)) = extract_sides_of_xor_assign(s1, ctxt);
+            if let Some((lhs1, rhs1)) = extract_sides_of_xor_assign(s2, ctxt);
+            if let Some((lhs2, rhs2)) = extract_sides_of_xor_assign(s3, ctxt);
             if eq_expr_value(cx, lhs0, rhs1);
             if eq_expr_value(cx, lhs2, rhs1);
             if eq_expr_value(cx, lhs1, rhs0);
             if eq_expr_value(cx, lhs1, rhs2);
+            if s2.span.ctxt() == ctxt;
+            if s3.span.ctxt() == ctxt;
             then {
-                let span = window[0].span.to(window[2].span);
+                let span = s1.span.to(s3.span);
                 generate_swap_warning(cx, lhs0, rhs0, span, true);
             }
         };
@@ -264,9 +277,12 @@ fn check_xor_swap(cx: &LateContext<'_>, block: &Block<'_>) {
 }
 
 /// Returns the lhs and rhs of an xor assignment statement.
-fn extract_sides_of_xor_assign<'a, 'hir>(stmt: &'a Stmt<'hir>) -> Option<(&'a Expr<'hir>, &'a Expr<'hir>)> {
-    if let StmtKind::Semi(expr) = stmt.kind {
-        if let ExprKind::AssignOp(
+fn extract_sides_of_xor_assign<'a, 'hir>(
+    stmt: &'a Stmt<'hir>,
+    ctxt: SyntaxContext,
+) -> Option<(&'a Expr<'hir>, &'a Expr<'hir>)> {
+    if let StmtKind::Semi(expr) = stmt.kind
+        && let ExprKind::AssignOp(
             Spanned {
                 node: BinOpKind::BitXor,
                 ..
@@ -274,9 +290,10 @@ fn extract_sides_of_xor_assign<'a, 'hir>(stmt: &'a Stmt<'hir>) -> Option<(&'a Ex
             lhs,
             rhs,
         ) = expr.kind
-        {
-            return Some((lhs, rhs));
-        }
+        && expr.span.ctxt() == ctxt
+    {
+        Some((lhs, rhs))
+    } else {
+        None
     }
-    None
 }
diff --git a/src/tools/clippy/clippy_lints/src/transmute/mod.rs b/src/tools/clippy/clippy_lints/src/transmute/mod.rs
index c01cbe5090f..0dc30f7a935 100644
--- a/src/tools/clippy/clippy_lints/src/transmute/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/transmute/mod.rs
@@ -458,7 +458,7 @@ declare_clippy_lint! {
     /// ```rust
     /// let null_fn: Option<fn()> = None;
     /// ```
-    #[clippy::version = "1.67.0"]
+    #[clippy::version = "1.68.0"]
     pub TRANSMUTE_NULL_TO_FN,
     correctness,
     "transmute results in a null function pointer, which is undefined behavior"
diff --git a/src/tools/clippy/clippy_lints/src/transmute/utils.rs b/src/tools/clippy/clippy_lints/src/transmute/utils.rs
index cddaf9450ea..62efd13b8d9 100644
--- a/src/tools/clippy/clippy_lints/src/transmute/utils.rs
+++ b/src/tools/clippy/clippy_lints/src/transmute/utils.rs
@@ -33,38 +33,37 @@ pub(super) fn check_cast<'tcx>(
     let hir_id = e.hir_id;
     let local_def_id = hir_id.owner.def_id;
 
-    Inherited::build(cx.tcx, local_def_id).enter(|inherited| {
-        let fn_ctxt = FnCtxt::new(inherited, cx.param_env, local_def_id);
+    let inherited = Inherited::new(cx.tcx, local_def_id);
+    let fn_ctxt = FnCtxt::new(&inherited, cx.param_env, local_def_id);
 
-        // If we already have errors, we can't be sure we can pointer cast.
+    // If we already have errors, we can't be sure we can pointer cast.
+    assert!(
+        !fn_ctxt.errors_reported_since_creation(),
+        "Newly created FnCtxt contained errors"
+    );
+
+    if let Ok(check) = cast::CastCheck::new(
+        &fn_ctxt,
+        e,
+        from_ty,
+        to_ty,
+        // We won't show any error to the user, so we don't care what the span is here.
+        DUMMY_SP,
+        DUMMY_SP,
+        hir::Constness::NotConst,
+    ) {
+        let res = check.do_check(&fn_ctxt);
+
+        // do_check's documentation says that it might return Ok and create
+        // errors in the fcx instead of returning Err in some cases. Those cases
+        // should be filtered out before getting here.
         assert!(
             !fn_ctxt.errors_reported_since_creation(),
-            "Newly created FnCtxt contained errors"
+            "`fn_ctxt` contained errors after cast check!"
         );
 
-        if let Ok(check) = cast::CastCheck::new(
-            &fn_ctxt,
-            e,
-            from_ty,
-            to_ty,
-            // We won't show any error to the user, so we don't care what the span is here.
-            DUMMY_SP,
-            DUMMY_SP,
-            hir::Constness::NotConst,
-        ) {
-            let res = check.do_check(&fn_ctxt);
-
-            // do_check's documentation says that it might return Ok and create
-            // errors in the fcx instead of returning Err in some cases. Those cases
-            // should be filtered out before getting here.
-            assert!(
-                !fn_ctxt.errors_reported_since_creation(),
-                "`fn_ctxt` contained errors after cast check!"
-            );
-
-            res.ok()
-        } else {
-            None
-        }
-    })
+        res.ok()
+    } else {
+        None
+    }
 }
diff --git a/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs b/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs
index d6167a62169..3430b6e3734 100644
--- a/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs
+++ b/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs
@@ -5,7 +5,7 @@ use clippy_utils::visitors::{for_each_local_assignment, for_each_value_source};
 use core::ops::ControlFlow;
 use rustc_errors::Applicability;
 use rustc_hir::def::{DefKind, Res};
-use rustc_hir::{Expr, ExprKind, HirId, HirIdSet, Local, Node, PatKind, QPath, TyKind};
+use rustc_hir::{Expr, ExprKind, HirId, HirIdSet, Local, MatchSource, Node, PatKind, QPath, TyKind};
 use rustc_lint::{LateContext, LintContext};
 use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty;
@@ -41,6 +41,10 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, local: &'tcx Local<'_>) {
                 );
             }
         } else {
+            if let ExprKind::Match(_, _, MatchSource::AwaitDesugar) = init.kind {
+                return
+            }
+
             span_lint_and_then(
                 cx,
                 LET_UNIT_VALUE,
diff --git a/src/tools/clippy/clippy_lints/src/use_self.rs b/src/tools/clippy/clippy_lints/src/use_self.rs
index e7c54000684..8ea5475fb25 100644
--- a/src/tools/clippy/clippy_lints/src/use_self.rs
+++ b/src/tools/clippy/clippy_lints/src/use_self.rs
@@ -10,8 +10,8 @@ use rustc_hir::{
     def::{CtorOf, DefKind, Res},
     def_id::LocalDefId,
     intravisit::{walk_inf, walk_ty, Visitor},
-    Expr, ExprKind, FnRetTy, FnSig, GenericArg, HirId, Impl, ImplItemKind, Item, ItemKind, Pat, PatKind, Path, QPath,
-    TyKind,
+    Expr, ExprKind, FnRetTy, FnSig, GenericArg, GenericParam, GenericParamKind, HirId, Impl, ImplItemKind, Item,
+    ItemKind, Pat, PatKind, Path, QPath, Ty, TyKind,
 };
 use rustc_hir_analysis::hir_ty_to_ty;
 use rustc_lint::{LateContext, LateLintPass};
@@ -96,7 +96,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
         // avoid linting on nested items, we push `StackItem::NoCheck` on the stack to signal, that
         // we're in an `impl` or nested item, that we don't want to lint
         let stack_item = if_chain! {
-            if let ItemKind::Impl(Impl { self_ty, .. }) = item.kind;
+            if let ItemKind::Impl(Impl { self_ty, generics,.. }) = item.kind;
             if let TyKind::Path(QPath::Resolved(_, item_path)) = self_ty.kind;
             let parameters = &item_path.segments.last().expect(SEGMENTS_MSG).args;
             if parameters.as_ref().map_or(true, |params| {
@@ -105,10 +105,17 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
             if !item.span.from_expansion();
             if !is_from_proc_macro(cx, item); // expensive, should be last check
             then {
+                // Self cannot be used inside const generic parameters
+                let types_to_skip = generics.params.iter().filter_map(|param| {
+                    match param {
+                        GenericParam { kind: GenericParamKind::Const { ty: Ty { hir_id, ..}, ..}, ..} => Some(*hir_id),
+                        _ => None,
+                    }
+                }).chain(std::iter::once(self_ty.hir_id)).collect();
                 StackItem::Check {
                     impl_id: item.owner_id.def_id,
                     in_body: 0,
-                    types_to_skip: std::iter::once(self_ty.hir_id).collect(),
+                    types_to_skip,
                 }
             } else {
                 StackItem::NoCheck
diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs
index c37e5bb6716..f31c3fdb095 100644
--- a/src/tools/clippy/clippy_lints/src/utils/author.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/author.rs
@@ -588,7 +588,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
                     },
                 }
             },
-            ExprKind::Err(_) => kind!("Err"),
+            ExprKind::Err(_) => kind!("Err(_)"),
             ExprKind::DropTemps(expr) => {
                 bind!(self, expr);
                 kind!("DropTemps({expr})");
diff --git a/src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs b/src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs
new file mode 100644
index 00000000000..be56b842b98
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs
@@ -0,0 +1,23 @@
+use clippy_utils::macros::collect_ast_format_args;
+use rustc_ast::{Expr, ExprKind};
+use rustc_lint::{EarlyContext, EarlyLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Collects [`rustc_ast::FormatArgs`] so that future late passes can call
+    /// [`clippy_utils::macros::find_format_args`]
+    pub FORMAT_ARGS_COLLECTOR,
+    internal_warn,
+    "collects `format_args` AST nodes for use in later lints"
+}
+
+declare_lint_pass!(FormatArgsCollector => [FORMAT_ARGS_COLLECTOR]);
+
+impl EarlyLintPass for FormatArgsCollector {
+    fn check_expr(&mut self, _: &EarlyContext<'_>, expr: &Expr) {
+        if let ExprKind::FormatArgs(args) = &expr.kind {
+            collect_ast_format_args(expr.span, args);
+        }
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs
index b1b5164ffb3..3d0d4a52511 100644
--- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs
@@ -26,7 +26,7 @@ use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::symbol::Ident;
 use rustc_span::{sym, Loc, Span, Symbol};
 use serde::{ser::SerializeStruct, Serialize, Serializer};
-use std::collections::BinaryHeap;
+use std::collections::{BTreeSet, BinaryHeap};
 use std::fmt;
 use std::fmt::Write as _;
 use std::fs::{self, OpenOptions};
@@ -264,6 +264,9 @@ struct LintMetadata {
     /// This field is only used in the output and will only be
     /// mapped shortly before the actual output.
     applicability: Option<ApplicabilityInfo>,
+    /// All the past names of lints which have been renamed.
+    #[serde(skip_serializing_if = "BTreeSet::is_empty")]
+    former_ids: BTreeSet<String>,
 }
 
 impl LintMetadata {
@@ -283,6 +286,7 @@ impl LintMetadata {
             version,
             docs,
             applicability: None,
+            former_ids: BTreeSet::new(),
         }
     }
 }
@@ -901,6 +905,7 @@ fn collect_renames(lints: &mut Vec<LintMetadata>) {
                         if name == lint_name;
                         if let Some(past_name) = k.strip_prefix(CLIPPY_LINT_GROUP_PREFIX);
                         then {
+                            lint.former_ids.insert(past_name.to_owned());
                             writeln!(collected, "* `{past_name}`").unwrap();
                             names.push(past_name.to_string());
                         }
diff --git a/src/tools/clippy/clippy_lints/src/utils/mod.rs b/src/tools/clippy/clippy_lints/src/utils/mod.rs
index 787e9fd982c..dc647af264c 100644
--- a/src/tools/clippy/clippy_lints/src/utils/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/mod.rs
@@ -1,5 +1,6 @@
 pub mod author;
 pub mod conf;
 pub mod dump_hir;
+pub mod format_args_collector;
 #[cfg(feature = "internal")]
 pub mod internal_lints;
diff --git a/src/tools/clippy/clippy_lints/src/write.rs b/src/tools/clippy/clippy_lints/src/write.rs
index df335038881..8114a8463fa 100644
--- a/src/tools/clippy/clippy_lints/src/write.rs
+++ b/src/tools/clippy/clippy_lints/src/write.rs
@@ -1,10 +1,11 @@
 use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
-use clippy_utils::macros::{root_macro_call_first_node, FormatArgsExpn, MacroCall};
+use clippy_utils::macros::{find_format_args, format_arg_removal_span, root_macro_call_first_node, MacroCall};
 use clippy_utils::source::{expand_past_previous_comma, snippet_opt};
 use clippy_utils::{is_in_cfg_test, is_in_test_function};
-use rustc_ast::LitKind;
+use rustc_ast::token::LitKind;
+use rustc_ast::{FormatArgPosition, FormatArgs, FormatArgsPiece, FormatOptions, FormatPlaceholder, FormatTrait};
 use rustc_errors::Applicability;
-use rustc_hir::{Expr, ExprKind, HirIdMap, Impl, Item, ItemKind};
+use rustc_hir::{Expr, Impl, Item, ItemKind};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::{sym, BytePos};
@@ -297,34 +298,40 @@ impl<'tcx> LateLintPass<'tcx> for Write {
             _ => return,
         }
 
-        let Some(format_args) = FormatArgsExpn::find_nested(cx, expr, macro_call.expn) else { return };
-
-        // ignore `writeln!(w)` and `write!(v, some_macro!())`
-        if format_args.format_string.span.from_expansion() {
-            return;
-        }
+        find_format_args(cx, expr, macro_call.expn, |format_args| {
+            // ignore `writeln!(w)` and `write!(v, some_macro!())`
+            if format_args.span.from_expansion() {
+                return;
+            }
 
-        match diag_name {
-            sym::print_macro | sym::eprint_macro | sym::write_macro => {
-                check_newline(cx, &format_args, &macro_call, name);
-            },
-            sym::println_macro | sym::eprintln_macro | sym::writeln_macro => {
-                check_empty_string(cx, &format_args, &macro_call, name);
-            },
-            _ => {},
-        }
+            match diag_name {
+                sym::print_macro | sym::eprint_macro | sym::write_macro => {
+                    check_newline(cx, format_args, &macro_call, name);
+                },
+                sym::println_macro | sym::eprintln_macro | sym::writeln_macro => {
+                    check_empty_string(cx, format_args, &macro_call, name);
+                },
+                _ => {},
+            }
 
-        check_literal(cx, &format_args, name);
+            check_literal(cx, format_args, name);
 
-        if !self.in_debug_impl {
-            for arg in &format_args.args {
-                if arg.format.r#trait == sym::Debug {
-                    span_lint(cx, USE_DEBUG, arg.span, "use of `Debug`-based formatting");
+            if !self.in_debug_impl {
+                for piece in &format_args.template {
+                    if let &FormatArgsPiece::Placeholder(FormatPlaceholder {
+                        span: Some(span),
+                        format_trait: FormatTrait::Debug,
+                        ..
+                    }) = piece
+                    {
+                        span_lint(cx, USE_DEBUG, span, "use of `Debug`-based formatting");
+                    }
                 }
             }
-        }
+        });
     }
 }
+
 fn is_debug_impl(cx: &LateContext<'_>, item: &Item<'_>) -> bool {
     if let ItemKind::Impl(Impl { of_trait: Some(trait_ref), .. }) = &item.kind
         && let Some(trait_id) = trait_ref.trait_def_id()
@@ -335,16 +342,18 @@ fn is_debug_impl(cx: &LateContext<'_>, item: &Item<'_>) -> bool {
     }
 }
 
-fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, macro_call: &MacroCall, name: &str) {
-    let format_string_parts = &format_args.format_string.parts;
-    let mut format_string_span = format_args.format_string.span;
-
-    let Some(last) = format_string_parts.last() else { return };
+fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgs, macro_call: &MacroCall, name: &str) {
+    let Some(FormatArgsPiece::Literal(last)) = format_args.template.last() else { return };
 
     let count_vertical_whitespace = || {
-        format_string_parts
+        format_args
+            .template
             .iter()
-            .flat_map(|part| part.as_str().chars())
+            .filter_map(|piece| match piece {
+                FormatArgsPiece::Literal(literal) => Some(literal),
+                FormatArgsPiece::Placeholder(_) => None,
+            })
+            .flat_map(|literal| literal.as_str().chars())
             .filter(|ch| matches!(ch, '\r' | '\n'))
             .count()
     };
@@ -352,10 +361,9 @@ fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, macro_c
     if last.as_str().ends_with('\n')
         // ignore format strings with other internal vertical whitespace
         && count_vertical_whitespace() == 1
-
-        // ignore trailing arguments: `print!("Issue\n{}", 1265);`
-        && format_string_parts.len() > format_args.args.len()
     {
+        let mut format_string_span = format_args.span;
+
         let lint = if name == "write" {
             format_string_span = expand_past_previous_comma(cx, format_string_span);
 
@@ -373,7 +381,7 @@ fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, macro_c
                 let name_span = cx.sess().source_map().span_until_char(macro_call.span, '!');
                 let Some(format_snippet) = snippet_opt(cx, format_string_span) else { return };
 
-                if format_string_parts.len() == 1 && last.as_str() == "\n" {
+                if format_args.template.len() == 1 && last.as_str() == "\n" {
                     // print!("\n"), write!(f, "\n")
 
                     diag.multipart_suggestion(
@@ -398,11 +406,12 @@ fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, macro_c
     }
 }
 
-fn check_empty_string(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, macro_call: &MacroCall, name: &str) {
-    if let [part] = &format_args.format_string.parts[..]
-        && let mut span = format_args.format_string.span
-        && part.as_str() == "\n"
+fn check_empty_string(cx: &LateContext<'_>, format_args: &FormatArgs, macro_call: &MacroCall, name: &str) {
+    if let [FormatArgsPiece::Literal(literal)] = &format_args.template[..]
+        && literal.as_str() == "\n"
     {
+        let mut span = format_args.span;
+
         let lint = if name == "writeln" {
             span = expand_past_previous_comma(cx, span);
 
@@ -428,33 +437,43 @@ fn check_empty_string(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, ma
     }
 }
 
-fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, name: &str) {
-    let mut counts = HirIdMap::<usize>::default();
-    for param in format_args.params() {
-        *counts.entry(param.value.hir_id).or_default() += 1;
+fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) {
+    let arg_index = |argument: &FormatArgPosition| argument.index.unwrap_or_else(|pos| pos);
+
+    let mut counts = vec![0u32; format_args.arguments.all_args().len()];
+    for piece in &format_args.template {
+        if let FormatArgsPiece::Placeholder(placeholder) = piece {
+            counts[arg_index(&placeholder.argument)] += 1;
+        }
     }
 
-    for arg in &format_args.args {
-        let value = arg.param.value;
-
-        if counts[&value.hir_id] == 1
-            && arg.format.is_default()
-            && let ExprKind::Lit(lit) = &value.kind
-            && !value.span.from_expansion()
-            && let Some(value_string) = snippet_opt(cx, value.span)
-        {
-            let (replacement, replace_raw) = match lit.node {
-                LitKind::Str(..) => extract_str_literal(&value_string),
-                LitKind::Char(ch) => (
-                    match ch {
-                        '"' => "\\\"",
-                        '\'' => "'",
+    for piece in &format_args.template {
+        if let FormatArgsPiece::Placeholder(FormatPlaceholder {
+            argument,
+            span: Some(placeholder_span),
+            format_trait: FormatTrait::Display,
+            format_options,
+        }) = piece
+            && *format_options == FormatOptions::default()
+            && let index = arg_index(argument)
+            && counts[index] == 1
+            && let Some(arg) = format_args.arguments.by_index(index)
+            && let rustc_ast::ExprKind::Lit(lit) = &arg.expr.kind
+            && !arg.expr.span.from_expansion()
+            && let Some(value_string) = snippet_opt(cx, arg.expr.span)
+    {
+            let (replacement, replace_raw) = match lit.kind {
+                LitKind::Str | LitKind::StrRaw(_)  => extract_str_literal(&value_string),
+                LitKind::Char => (
+                    match lit.symbol.as_str() {
+                        "\"" => "\\\"",
+                        "\\'" => "'",
                         _ => &value_string[1..value_string.len() - 1],
                     }
                     .to_string(),
                     false,
                 ),
-                LitKind::Bool(b) => (b.to_string(), false),
+                LitKind::Bool => (lit.symbol.to_string(), false),
                 _ => continue,
             };
 
@@ -464,7 +483,9 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, name: &
                 PRINT_LITERAL
             };
 
-            let format_string_is_raw = format_args.format_string.style.is_some();
+            let Some(format_string_snippet) = snippet_opt(cx, format_args.span) else { continue };
+            let format_string_is_raw = format_string_snippet.starts_with('r');
+
             let replacement = match (format_string_is_raw, replace_raw) {
                 (false, false) => Some(replacement),
                 (false, true) => Some(replacement.replace('"', "\\\"").replace('\\', "\\\\")),
@@ -485,23 +506,24 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, name: &
             span_lint_and_then(
                 cx,
                 lint,
-                value.span,
+                arg.expr.span,
                 "literal with an empty format string",
                 |diag| {
                     if let Some(replacement) = replacement
                         // `format!("{}", "a")`, `format!("{named}", named = "b")
                         //              ~~~~~                      ~~~~~~~~~~~~~
-                        && let Some(value_span) = format_args.value_with_prev_comma_span(value.hir_id)
+                        && let Some(removal_span) = format_arg_removal_span(format_args, index)
                     {
                         let replacement = replacement.replace('{', "{{").replace('}', "}}");
                         diag.multipart_suggestion(
                             "try this",
-                            vec![(arg.span, replacement), (value_span, String::new())],
+                            vec![(*placeholder_span, replacement), (removal_span, String::new())],
                             Applicability::MachineApplicable,
                         );
                     }
                 },
             );
+
         }
     }
 }
diff --git a/src/tools/clippy/clippy_utils/Cargo.toml b/src/tools/clippy/clippy_utils/Cargo.toml
index 173469f6cdc..124ebd164e6 100644
--- a/src/tools/clippy/clippy_utils/Cargo.toml
+++ b/src/tools/clippy/clippy_utils/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "clippy_utils"
-version = "0.1.69"
+version = "0.1.70"
 edition = "2021"
 publish = false
 
diff --git a/src/tools/clippy/clippy_utils/src/ast_utils.rs b/src/tools/clippy/clippy_utils/src/ast_utils.rs
index d82098523e3..809d654603a 100644
--- a/src/tools/clippy/clippy_utils/src/ast_utils.rs
+++ b/src/tools/clippy/clippy_utils/src/ast_utils.rs
@@ -143,7 +143,7 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool {
         (Paren(l), _) => eq_expr(l, r),
         (_, Paren(r)) => eq_expr(l, r),
         (Err, Err) => true,
-        (Box(l), Box(r)) | (Try(l), Try(r)) | (Await(l), Await(r)) => eq_expr(l, r),
+        (Try(l), Try(r)) | (Await(l), Await(r)) => eq_expr(l, r),
         (Array(l), Array(r)) => over(l, r, |l, r| eq_expr(l, r)),
         (Tup(l), Tup(r)) => over(l, r, |l, r| eq_expr(l, r)),
         (Repeat(le, ls), Repeat(re, rs)) => eq_expr(le, re) && eq_expr(&ls.value, &rs.value),
diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs
index bcfedd07ed1..29830557a44 100644
--- a/src/tools/clippy/clippy_utils/src/lib.rs
+++ b/src/tools/clippy/clippy_utils/src/lib.rs
@@ -617,7 +617,7 @@ fn item_children_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: Symbol) -> Vec<Re
 /// Can return multiple resolutions when there are multiple versions of the same crate, e.g.
 /// `memchr::memchr` could return the functions from both memchr 1.0 and memchr 2.0.
 ///
-/// Also returns multiple results when there are mulitple paths under the same name e.g. `std::vec`
+/// Also returns multiple results when there are multiple paths under the same name e.g. `std::vec`
 /// would have both a [`DefKind::Mod`] and [`DefKind::Macro`].
 ///
 /// This function is expensive and should be used sparingly.
@@ -1904,16 +1904,7 @@ pub fn is_async_fn(kind: FnKind<'_>) -> bool {
 
 /// Peels away all the compiler generated code surrounding the body of an async function,
 pub fn get_async_fn_body<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'tcx Expr<'tcx>> {
-    if let ExprKind::Call(
-        _,
-        &[
-            Expr {
-                kind: ExprKind::Closure(&Closure { body, .. }),
-                ..
-            },
-        ],
-    ) = body.value.kind
-    {
+    if let ExprKind::Closure(&Closure { body, .. }) = body.value.kind {
         if let ExprKind::Block(
             Block {
                 stmts: [],
diff --git a/src/tools/clippy/clippy_utils/src/macros.rs b/src/tools/clippy/clippy_utils/src/macros.rs
index be6133d3202..e135bd9feee 100644
--- a/src/tools/clippy/clippy_utils/src/macros.rs
+++ b/src/tools/clippy/clippy_utils/src/macros.rs
@@ -6,6 +6,8 @@ use crate::visitors::{for_each_expr, Descend};
 use arrayvec::ArrayVec;
 use itertools::{izip, Either, Itertools};
 use rustc_ast::ast::LitKind;
+use rustc_ast::FormatArgs;
+use rustc_data_structures::fx::FxHashMap;
 use rustc_hir::intravisit::{walk_expr, Visitor};
 use rustc_hir::{self as hir, Expr, ExprField, ExprKind, HirId, LangItem, Node, QPath, TyKind};
 use rustc_lexer::unescape::unescape_literal;
@@ -15,8 +17,10 @@ use rustc_parse_format::{self as rpf, Alignment};
 use rustc_span::def_id::DefId;
 use rustc_span::hygiene::{self, MacroKind, SyntaxContext};
 use rustc_span::{sym, BytePos, ExpnData, ExpnId, ExpnKind, Pos, Span, SpanData, Symbol};
+use std::cell::RefCell;
 use std::iter::{once, zip};
 use std::ops::ControlFlow;
+use std::sync::atomic::{AtomicBool, Ordering};
 
 const FORMAT_MACRO_DIAG_ITEMS: &[Symbol] = &[
     sym::assert_eq_macro,
@@ -213,6 +217,7 @@ pub fn is_assert_macro(cx: &LateContext<'_>, def_id: DefId) -> bool {
     matches!(name, sym::assert_macro | sym::debug_assert_macro)
 }
 
+#[derive(Debug)]
 pub enum PanicExpn<'a> {
     /// No arguments - `panic!()`
     Empty,
@@ -226,10 +231,7 @@ pub enum PanicExpn<'a> {
 
 impl<'a> PanicExpn<'a> {
     pub fn parse(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<Self> {
-        if !macro_backtrace(expr.span).any(|macro_call| is_panic(cx, macro_call.def_id)) {
-            return None;
-        }
-        let ExprKind::Call(callee, [arg]) = &expr.kind else { return None };
+        let ExprKind::Call(callee, [arg, rest @ ..]) = &expr.kind else { return None };
         let ExprKind::Path(QPath::Resolved(_, path)) = &callee.kind else { return None };
         let result = match path.segments.last().unwrap().ident.as_str() {
             "panic" if arg.span.ctxt() == expr.span.ctxt() => Self::Empty,
@@ -239,6 +241,21 @@ impl<'a> PanicExpn<'a> {
                 Self::Display(e)
             },
             "panic_fmt" => Self::Format(FormatArgsExpn::parse(cx, arg)?),
+            // Since Rust 1.52, `assert_{eq,ne}` macros expand to use:
+            // `core::panicking::assert_failed(.., left_val, right_val, None | Some(format_args!(..)));`
+            "assert_failed" => {
+                // It should have 4 arguments in total (we already matched with the first argument,
+                // so we're just checking for 3)
+                if rest.len() != 3 {
+                    return None;
+                }
+                // `msg_arg` is either `None` (no custom message) or `Some(format_args!(..))` (custom message)
+                let msg_arg = &rest[2];
+                match msg_arg.kind {
+                    ExprKind::Call(_, [fmt_arg]) => Self::Format(FormatArgsExpn::parse(cx, fmt_arg)?),
+                    _ => Self::Empty,
+                }
+            },
             _ => return None,
         };
         Some(result)
@@ -251,7 +268,17 @@ pub fn find_assert_args<'a>(
     expr: &'a Expr<'a>,
     expn: ExpnId,
 ) -> Option<(&'a Expr<'a>, PanicExpn<'a>)> {
-    find_assert_args_inner(cx, expr, expn).map(|([e], p)| (e, p))
+    find_assert_args_inner(cx, expr, expn).map(|([e], mut p)| {
+        // `assert!(..)` expands to `core::panicking::panic("assertion failed: ...")` (which we map to
+        // `PanicExpn::Str(..)`) and `assert!(.., "..")` expands to
+        // `core::panicking::panic_fmt(format_args!(".."))` (which we map to `PanicExpn::Format(..)`).
+        // So even we got `PanicExpn::Str(..)` that means there is no custom message provided
+        if let PanicExpn::Str(_) = p {
+            p = PanicExpn::Empty;
+        }
+
+        (e, p)
+    })
 }
 
 /// Finds the arguments of an `assert_eq!` or `debug_assert_eq!` macro call within the macro
@@ -275,13 +302,12 @@ fn find_assert_args_inner<'a, const N: usize>(
         Some(inner_name) => find_assert_within_debug_assert(cx, expr, expn, Symbol::intern(inner_name))?,
     };
     let mut args = ArrayVec::new();
-    let mut panic_expn = None;
-    let _: Option<!> = for_each_expr(expr, |e| {
+    let panic_expn = for_each_expr(expr, |e| {
         if args.is_full() {
-            if panic_expn.is_none() && e.span.ctxt() != expr.span.ctxt() {
-                panic_expn = PanicExpn::parse(cx, e);
+            match PanicExpn::parse(cx, e) {
+                Some(expn) => ControlFlow::Break(expn),
+                None => ControlFlow::Continue(Descend::Yes),
             }
-            ControlFlow::Continue(Descend::from(panic_expn.is_none()))
         } else if is_assert_arg(cx, e, expn) {
             args.push(e);
             ControlFlow::Continue(Descend::No)
@@ -339,6 +365,77 @@ fn is_assert_arg(cx: &LateContext<'_>, expr: &Expr<'_>, assert_expn: ExpnId) ->
     }
 }
 
+thread_local! {
+    /// We preserve the [`FormatArgs`] structs from the early pass for use in the late pass to be
+    /// able to access the many features of a [`LateContext`].
+    ///
+    /// A thread local is used because [`FormatArgs`] is `!Send` and `!Sync`, we are making an
+    /// assumption that the early pass the populates the map and the later late passes will all be
+    /// running on the same thread.
+    static AST_FORMAT_ARGS: RefCell<FxHashMap<Span, FormatArgs>> = {
+        static CALLED: AtomicBool = AtomicBool::new(false);
+        debug_assert!(
+            !CALLED.swap(true, Ordering::SeqCst),
+            "incorrect assumption: `AST_FORMAT_ARGS` should only be accessed by a single thread",
+        );
+
+        RefCell::default()
+    };
+}
+
+/// Record [`rustc_ast::FormatArgs`] for use in late lint passes, this should only be called by
+/// `FormatArgsCollector`
+pub fn collect_ast_format_args(span: Span, format_args: &FormatArgs) {
+    AST_FORMAT_ARGS.with(|ast_format_args| {
+        ast_format_args.borrow_mut().insert(span, format_args.clone());
+    });
+}
+
+/// Calls `callback` with an AST [`FormatArgs`] node if one is found
+pub fn find_format_args(cx: &LateContext<'_>, start: &Expr<'_>, expn_id: ExpnId, callback: impl FnOnce(&FormatArgs)) {
+    let format_args_expr = for_each_expr(start, |expr| {
+        let ctxt = expr.span.ctxt();
+        if ctxt == start.span.ctxt() {
+            ControlFlow::Continue(Descend::Yes)
+        } else if ctxt.outer_expn().is_descendant_of(expn_id)
+            && macro_backtrace(expr.span)
+                .map(|macro_call| cx.tcx.item_name(macro_call.def_id))
+                .any(|name| matches!(name, sym::const_format_args | sym::format_args | sym::format_args_nl))
+        {
+            ControlFlow::Break(expr)
+        } else {
+            ControlFlow::Continue(Descend::No)
+        }
+    });
+
+    if let Some(format_args_expr) = format_args_expr {
+        AST_FORMAT_ARGS.with(|ast_format_args| {
+            ast_format_args.borrow().get(&format_args_expr.span).map(callback);
+        });
+    }
+}
+
+/// Returns the [`Span`] of the value at `index` extended to the previous comma, e.g. for the value
+/// `10`
+///
+/// ```ignore
+/// format("{}.{}", 10, 11)
+/// //            ^^^^
+/// ```
+pub fn format_arg_removal_span(format_args: &FormatArgs, index: usize) -> Option<Span> {
+    let ctxt = format_args.span.ctxt();
+
+    let current = hygiene::walk_chain(format_args.arguments.by_index(index)?.expr.span, ctxt);
+
+    let prev = if index == 0 {
+        format_args.span
+    } else {
+        hygiene::walk_chain(format_args.arguments.by_index(index - 1)?.expr.span, ctxt)
+    };
+
+    Some(current.with_lo(prev.hi()))
+}
+
 /// The format string doesn't exist in the HIR, so we reassemble it from source code
 #[derive(Debug)]
 pub struct FormatString {
diff --git a/src/tools/clippy/clippy_utils/src/sugg.rs b/src/tools/clippy/clippy_utils/src/sugg.rs
index 07feadca2b0..85bf28b708b 100644
--- a/src/tools/clippy/clippy_utils/src/sugg.rs
+++ b/src/tools/clippy/clippy_utils/src/sugg.rs
@@ -188,7 +188,6 @@ impl<'a> Sugg<'a> {
         match expr.kind {
             _ if expr.span.ctxt() != ctxt => Sugg::NonParen(snippet_with_context(cx, expr.span, ctxt, default, app).0),
             ast::ExprKind::AddrOf(..)
-            | ast::ExprKind::Box(..)
             | ast::ExprKind::Closure { .. }
             | ast::ExprKind::If(..)
             | ast::ExprKind::Let(..)
diff --git a/src/tools/clippy/clippy_utils/src/ty.rs b/src/tools/clippy/clippy_utils/src/ty.rs
index 41e34eba0ad..e0ea3952785 100644
--- a/src/tools/clippy/clippy_utils/src/ty.rs
+++ b/src/tools/clippy/clippy_utils/src/ty.rs
@@ -16,9 +16,9 @@ use rustc_infer::infer::{
 use rustc_lint::LateContext;
 use rustc_middle::mir::interpret::{ConstValue, Scalar};
 use rustc_middle::ty::{
-    self, AdtDef, AliasTy, AssocKind, Binder, BoundRegion, FnSig, IntTy, List, ParamEnv, Predicate,
-    PredicateKind, Region, RegionKind, SubstsRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt,
-    TypeVisitor, UintTy, VariantDef, VariantDiscr,
+    self, AdtDef, AliasTy, AssocKind, Binder, BoundRegion, FnSig, IntTy, List, ParamEnv, Predicate, PredicateKind,
+    Region, RegionKind, SubstsRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor,
+    UintTy, VariantDef, VariantDiscr,
 };
 use rustc_middle::ty::{GenericArg, GenericArgKind};
 use rustc_span::symbol::Ident;
diff --git a/src/tools/clippy/declare_clippy_lint/Cargo.toml b/src/tools/clippy/declare_clippy_lint/Cargo.toml
index 80eee368178..5c9f76dbbc6 100644
--- a/src/tools/clippy/declare_clippy_lint/Cargo.toml
+++ b/src/tools/clippy/declare_clippy_lint/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "declare_clippy_lint"
-version = "0.1.69"
+version = "0.1.70"
 edition = "2021"
 publish = false
 
diff --git a/src/tools/clippy/lintcheck/Cargo.toml b/src/tools/clippy/lintcheck/Cargo.toml
index 653121af54d..27d32f39003 100644
--- a/src/tools/clippy/lintcheck/Cargo.toml
+++ b/src/tools/clippy/lintcheck/Cargo.toml
@@ -8,12 +8,16 @@ repository = "https://github.com/rust-lang/rust-clippy"
 categories = ["development-tools"]
 edition = "2021"
 publish = false
+default-run = "lintcheck"
 
 [dependencies]
+anyhow = "1.0.69"
 cargo_metadata = "0.15.3"
-clap = "4.1.4"
+clap = { version = "4.1.8", features = ["derive", "env"] }
+crates_io_api = "0.8.1"
 crossbeam-channel = "0.5.6"
 flate2 = "1.0"
+indicatif = "0.17.3"
 rayon = "1.5.1"
 serde = { version = "1.0", features = ["derive"] }
 serde_json = "1.0.85"
@@ -24,3 +28,11 @@ walkdir = "2.3"
 
 [features]
 deny-warnings = []
+
+[[bin]]
+name = "lintcheck"
+path = "src/main.rs"
+
+[[bin]]
+name = "popular-crates"
+path = "src/popular-crates.rs"
diff --git a/src/tools/clippy/lintcheck/README.md b/src/tools/clippy/lintcheck/README.md
index 6142de5e313..e997eb47e32 100644
--- a/src/tools/clippy/lintcheck/README.md
+++ b/src/tools/clippy/lintcheck/README.md
@@ -25,6 +25,15 @@ the repo root.
 
 The results will then be saved to `lintcheck-logs/custom_logs.toml`.
 
+The `custom.toml` file may be built using <https://crates.io> recently most
+downloaded crates by using the `popular-crates` binary from the `lintcheck`
+directory. For example, to retrieve the 100 recently most downloaded crates:
+
+```
+cargo run --release --bin popular-crates -- -n 100 custom.toml
+```
+
+
 ### Configuring the Crate Sources
 
 The sources to check are saved in a `toml` file. There are three types of
diff --git a/src/tools/clippy/lintcheck/src/config.rs b/src/tools/clippy/lintcheck/src/config.rs
index e0244ddcecb..3f01e9bb0a7 100644
--- a/src/tools/clippy/lintcheck/src/config.rs
+++ b/src/tools/clippy/lintcheck/src/config.rs
@@ -1,131 +1,79 @@
-use clap::{Arg, ArgAction, ArgMatches, Command};
-use std::env;
-use std::path::PathBuf;
+use clap::Parser;
+use std::{num::NonZeroUsize, path::PathBuf};
 
-fn get_clap_config() -> ArgMatches {
-    Command::new("lintcheck")
-        .about("run clippy on a set of crates and check output")
-        .args([
-            Arg::new("only")
-                .action(ArgAction::Set)
-                .value_name("CRATE")
-                .long("only")
-                .help("Only process a single crate of the list"),
-            Arg::new("crates-toml")
-                .action(ArgAction::Set)
-                .value_name("CRATES-SOURCES-TOML-PATH")
-                .long("crates-toml")
-                .help("Set the path for a crates.toml where lintcheck should read the sources from"),
-            Arg::new("threads")
-                .action(ArgAction::Set)
-                .value_name("N")
-                .value_parser(clap::value_parser!(usize))
-                .short('j')
-                .long("jobs")
-                .help("Number of threads to use, 0 automatic choice"),
-            Arg::new("fix")
-                .long("fix")
-                .help("Runs cargo clippy --fix and checks if all suggestions apply"),
-            Arg::new("filter")
-                .long("filter")
-                .action(ArgAction::Append)
-                .value_name("clippy_lint_name")
-                .help("Apply a filter to only collect specified lints, this also overrides `allow` attributes"),
-            Arg::new("markdown")
-                .long("markdown")
-                .help("Change the reports table to use markdown links"),
-            Arg::new("recursive")
-                .long("recursive")
-                .help("Run clippy on the dependencies of crates specified in crates-toml")
-                .conflicts_with("threads")
-                .conflicts_with("fix"),
-        ])
-        .get_matches()
-}
-
-#[derive(Debug, Clone)]
+#[derive(Clone, Debug, Parser)]
 pub(crate) struct LintcheckConfig {
-    /// max number of jobs to spawn (default 1)
+    /// Number of threads to use (default: all unless --fix or --recursive)
+    #[clap(
+        long = "jobs",
+        short = 'j',
+        value_name = "N",
+        default_value_t = 0,
+        hide_default_value = true
+    )]
     pub max_jobs: usize,
-    /// we read the sources to check from here
+    /// Set the path for a crates.toml where lintcheck should read the sources from
+    #[clap(
+        long = "crates-toml",
+        value_name = "CRATES-SOURCES-TOML-PATH",
+        default_value = "lintcheck/lintcheck_crates.toml",
+        hide_default_value = true,
+        env = "LINTCHECK_TOML",
+        hide_env = true
+    )]
     pub sources_toml_path: PathBuf,
-    /// we save the clippy lint results here
-    pub lintcheck_results_path: PathBuf,
-    /// Check only a specified package
+    /// File to save the clippy lint results here
+    #[clap(skip = "")]
+    pub lintcheck_results_path: PathBuf, // Overridden in new()
+    /// Only process a single crate on the list
+    #[clap(long, value_name = "CRATE")]
     pub only: Option<String>,
-    /// whether to just run --fix and not collect all the warnings
+    /// Runs cargo clippy --fix and checks if all suggestions apply
+    #[clap(long, conflicts_with("max_jobs"))]
     pub fix: bool,
-    /// A list of lints that this lintcheck run should focus on
+    /// Apply a filter to only collect specified lints, this also overrides `allow` attributes
+    #[clap(long = "filter", value_name = "clippy_lint_name", use_value_delimiter = true)]
     pub lint_filter: Vec<String>,
-    /// Indicate if the output should support markdown syntax
+    /// Change the reports table to use markdown links
+    #[clap(long)]
     pub markdown: bool,
-    /// Run clippy on the dependencies of crates
+    /// Run clippy on the dependencies of crates specified in crates-toml
+    #[clap(long, conflicts_with("max_jobs"))]
     pub recursive: bool,
 }
 
 impl LintcheckConfig {
     pub fn new() -> Self {
-        let clap_config = get_clap_config();
-
-        // first, check if we got anything passed via the LINTCHECK_TOML env var,
-        // if not, ask clap if we got any value for --crates-toml  <foo>
-        // if not, use the default "lintcheck/lintcheck_crates.toml"
-        let sources_toml = env::var("LINTCHECK_TOML").unwrap_or_else(|_| {
-            clap_config
-                .get_one::<String>("crates-toml")
-                .map_or("lintcheck/lintcheck_crates.toml", |s| &**s)
-                .into()
-        });
-
-        let markdown = clap_config.contains_id("markdown");
-        let sources_toml_path = PathBuf::from(sources_toml);
+        let mut config = LintcheckConfig::parse();
 
         // for the path where we save the lint results, get the filename without extension (so for
         // wasd.toml, use "wasd"...)
-        let filename: PathBuf = sources_toml_path.file_stem().unwrap().into();
-        let lintcheck_results_path = PathBuf::from(format!(
+        let filename: PathBuf = config.sources_toml_path.file_stem().unwrap().into();
+        config.lintcheck_results_path = PathBuf::from(format!(
             "lintcheck-logs/{}_logs.{}",
             filename.display(),
-            if markdown { "md" } else { "txt" }
+            if config.markdown { "md" } else { "txt" }
         ));
 
-        // look at the --threads arg, if 0 is passed, ask rayon rayon how many threads it would spawn and
-        // use half of that for the physical core count
-        // by default use a single thread
-        let max_jobs = match clap_config.get_one::<usize>("threads") {
-            Some(&0) => {
-                // automatic choice
-                // Rayon seems to return thread count so half that for core count
-                rayon::current_num_threads() / 2
-            },
-            Some(&threads) => threads,
-            // no -j passed, use a single thread
-            None => 1,
+        // look at the --threads arg, if 0 is passed, use the threads count
+        if config.max_jobs == 0 {
+            config.max_jobs = if config.fix || config.recursive {
+                1
+            } else {
+                std::thread::available_parallelism().map_or(1, NonZeroUsize::get)
+            };
         };
 
-        let lint_filter: Vec<String> = clap_config
-            .get_many::<String>("filter")
-            .map(|iter| {
-                iter.map(|lint_name| {
-                    let mut filter = lint_name.replace('_', "-");
-                    if !filter.starts_with("clippy::") {
-                        filter.insert_str(0, "clippy::");
-                    }
-                    filter
-                })
-                .collect()
-            })
-            .unwrap_or_default();
-
-        LintcheckConfig {
-            max_jobs,
-            sources_toml_path,
-            lintcheck_results_path,
-            only: clap_config.get_one::<String>("only").map(String::from),
-            fix: clap_config.contains_id("fix"),
-            lint_filter,
-            markdown,
-            recursive: clap_config.contains_id("recursive"),
+        for lint_name in &mut config.lint_filter {
+            *lint_name = format!(
+                "clippy::{}",
+                lint_name
+                    .strip_prefix("clippy::")
+                    .unwrap_or(lint_name)
+                    .replace('_', "-")
+            );
         }
+
+        config
     }
 }
diff --git a/src/tools/clippy/lintcheck/src/popular-crates.rs b/src/tools/clippy/lintcheck/src/popular-crates.rs
new file mode 100644
index 00000000000..fdab984ad86
--- /dev/null
+++ b/src/tools/clippy/lintcheck/src/popular-crates.rs
@@ -0,0 +1,65 @@
+#![deny(clippy::pedantic)]
+
+use clap::Parser;
+use crates_io_api::{CratesQueryBuilder, Sort, SyncClient};
+use indicatif::ProgressBar;
+use std::collections::HashSet;
+use std::fs::File;
+use std::io::{BufWriter, Write};
+use std::path::PathBuf;
+use std::time::Duration;
+
+#[derive(Parser)]
+struct Opts {
+    /// Output TOML file name
+    output: PathBuf,
+    /// Number of crate names to download
+    #[clap(short, long, default_value_t = 100)]
+    number: usize,
+    /// Do not output progress
+    #[clap(short, long)]
+    quiet: bool,
+}
+
+fn main() -> anyhow::Result<()> {
+    let opts = Opts::parse();
+    let mut output = BufWriter::new(File::create(opts.output)?);
+    output.write_all(b"[crates]\n")?;
+    let client = SyncClient::new(
+        "clippy/lintcheck (github.com/rust-lang/rust-clippy/)",
+        Duration::from_secs(1),
+    )?;
+    let mut seen_crates = HashSet::new();
+    let pb = if opts.quiet {
+        None
+    } else {
+        Some(ProgressBar::new(opts.number as u64))
+    };
+    let mut query = CratesQueryBuilder::new()
+        .sort(Sort::RecentDownloads)
+        .page_size(100)
+        .build();
+    while seen_crates.len() < opts.number {
+        let retrieved = client.crates(query.clone())?.crates;
+        if retrieved.is_empty() {
+            eprintln!("No more than {} crates available from API", seen_crates.len());
+            break;
+        }
+        for c in retrieved {
+            if seen_crates.insert(c.name.clone()) {
+                output.write_all(
+                    format!(
+                        "{} = {{ name = '{}', versions = ['{}'] }}\n",
+                        c.name, c.name, c.max_version
+                    )
+                    .as_bytes(),
+                )?;
+                if let Some(pb) = &pb {
+                    pb.inc(1);
+                }
+            }
+        }
+        query.set_page(query.page() + 1);
+    }
+    Ok(())
+}
diff --git a/src/tools/clippy/rust-toolchain b/src/tools/clippy/rust-toolchain
index cfe845ec78f..d788c6359d7 100644
--- a/src/tools/clippy/rust-toolchain
+++ b/src/tools/clippy/rust-toolchain
@@ -1,3 +1,3 @@
 [toolchain]
-channel = "nightly-2023-02-25"
+channel = "nightly-2023-03-10"
 components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
diff --git a/src/tools/clippy/rustc_tools_util/README.md b/src/tools/clippy/rustc_tools_util/README.md
index eefc661f963..e197ea048a0 100644
--- a/src/tools/clippy/rustc_tools_util/README.md
+++ b/src/tools/clippy/rustc_tools_util/README.md
@@ -49,6 +49,8 @@ The changelog for `rustc_tools_util` is available under:
 
 ## License
 
+<!-- REUSE-IgnoreStart -->
+
 Copyright 2014-2022 The Rust Project Developers
 
 Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
@@ -56,3 +58,5 @@ http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
 <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
 option. All files in the project carrying such notice may not be
 copied, modified, or distributed except according to those terms.
+
+<!-- REUSE-IgnoreEnd -->
diff --git a/src/tools/clippy/src/driver.rs b/src/tools/clippy/src/driver.rs
index dd183362f27..f08393c303e 100644
--- a/src/tools/clippy/src/driver.rs
+++ b/src/tools/clippy/src/driver.rs
@@ -176,7 +176,7 @@ Common options:
         --rustc              Pass all args to rustc
     -V, --version            Print version info and exit
 
-Other options are the same as `cargo check`.
+For the other options see `cargo check --help`.
 
 To allow or deny a lint from the command line you can use `cargo clippy --`
 with:
diff --git a/src/tools/clippy/src/main.rs b/src/tools/clippy/src/main.rs
index 82147eba33f..c5e9b96cf3f 100644
--- a/src/tools/clippy/src/main.rs
+++ b/src/tools/clippy/src/main.rs
@@ -18,7 +18,7 @@ Common options:
     -V, --version            Print version info and exit
     --explain LINT           Print the documentation for a given lint
 
-Other options are the same as `cargo check`.
+For the other options see `cargo check --help`.
 
 To allow or deny a lint from the command line you can use `cargo clippy --`
 with:
diff --git a/src/tools/clippy/tests/dogfood.rs b/src/tools/clippy/tests/dogfood.rs
index 6d0022f7a5c..9643c2c9707 100644
--- a/src/tools/clippy/tests/dogfood.rs
+++ b/src/tools/clippy/tests/dogfood.rs
@@ -7,6 +7,7 @@
 #![cfg_attr(feature = "deny-warnings", deny(warnings))]
 #![warn(rust_2018_idioms, unused_lifetimes)]
 
+use itertools::Itertools;
 use std::path::PathBuf;
 use std::process::Command;
 use test_utils::IS_RUSTC_TEST_SUITE;
@@ -19,8 +20,10 @@ fn dogfood_clippy() {
         return;
     }
 
+    let mut failed_packages = Vec::new();
+
     // "" is the root package
-    for package in &[
+    for package in [
         "",
         "clippy_dev",
         "clippy_lints",
@@ -28,8 +31,16 @@ fn dogfood_clippy() {
         "lintcheck",
         "rustc_tools_util",
     ] {
-        run_clippy_for_package(package, &["-D", "clippy::all", "-D", "clippy::pedantic"]);
+        if !run_clippy_for_package(package, &["-D", "clippy::all", "-D", "clippy::pedantic"]) {
+            failed_packages.push(if package.is_empty() { "root" } else { package });
+        }
     }
+
+    assert!(
+        !failed_packages.is_empty(),
+        "Dogfood failed for packages `{}`",
+        failed_packages.iter().format(", "),
+    )
 }
 
 #[test]
@@ -71,7 +82,7 @@ fn run_metadata_collection_lint() {
     run_clippy_for_package("clippy_lints", &["-A", "unfulfilled_lint_expectations"]);
 }
 
-fn run_clippy_for_package(project: &str, args: &[&str]) {
+fn run_clippy_for_package(project: &str, args: &[&str]) -> bool {
     let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
 
     let mut command = Command::new(&*test_utils::CARGO_CLIPPY_PATH);
@@ -107,5 +118,5 @@ fn run_clippy_for_package(project: &str, args: &[&str]) {
     println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
     println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
 
-    assert!(output.status.success());
+    output.status.success()
 }
diff --git a/src/tools/clippy/tests/ui/arithmetic_side_effects.rs b/src/tools/clippy/tests/ui/arithmetic_side_effects.rs
index 2611e3a785f..ee7d2ba444b 100644
--- a/src/tools/clippy/tests/ui/arithmetic_side_effects.rs
+++ b/src/tools/clippy/tests/ui/arithmetic_side_effects.rs
@@ -45,24 +45,32 @@ impl_arith!(
     Div, Custom, Custom, div;
     Mul, Custom, Custom, mul;
     Rem, Custom, Custom, rem;
+    Shl, Custom, Custom, shl;
+    Shr, Custom, Custom, shr;
     Sub, Custom, Custom, sub;
 
     Add, Custom, &Custom, add;
     Div, Custom, &Custom, div;
     Mul, Custom, &Custom, mul;
     Rem, Custom, &Custom, rem;
+    Shl, Custom, &Custom, shl;
+    Shr, Custom, &Custom, shr;
     Sub, Custom, &Custom, sub;
 
     Add, &Custom, Custom, add;
     Div, &Custom, Custom, div;
     Mul, &Custom, Custom, mul;
     Rem, &Custom, Custom, rem;
+    Shl, &Custom, Custom, shl;
+    Shr, &Custom, Custom, shr;
     Sub, &Custom, Custom, sub;
 
     Add, &Custom, &Custom, add;
     Div, &Custom, &Custom, div;
     Mul, &Custom, &Custom, mul;
     Rem, &Custom, &Custom, rem;
+    Shl, &Custom, &Custom, shl;
+    Shr, &Custom, &Custom, shr;
     Sub, &Custom, &Custom, sub;
 );
 
@@ -71,24 +79,32 @@ impl_assign_arith!(
     DivAssign, Custom, Custom, div_assign;
     MulAssign, Custom, Custom, mul_assign;
     RemAssign, Custom, Custom, rem_assign;
+    ShlAssign, Custom, Custom, shl_assign;
+    ShrAssign, Custom, Custom, shr_assign;
     SubAssign, Custom, Custom, sub_assign;
 
     AddAssign, Custom, &Custom, add_assign;
     DivAssign, Custom, &Custom, div_assign;
     MulAssign, Custom, &Custom, mul_assign;
     RemAssign, Custom, &Custom, rem_assign;
+    ShlAssign, Custom, &Custom, shl_assign;
+    ShrAssign, Custom, &Custom, shr_assign;
     SubAssign, Custom, &Custom, sub_assign;
 
     AddAssign, &Custom, Custom, add_assign;
     DivAssign, &Custom, Custom, div_assign;
     MulAssign, &Custom, Custom, mul_assign;
     RemAssign, &Custom, Custom, rem_assign;
+    ShlAssign, &Custom, Custom, shl_assign;
+    ShrAssign, &Custom, Custom, shr_assign;
     SubAssign, &Custom, Custom, sub_assign;
 
     AddAssign, &Custom, &Custom, add_assign;
     DivAssign, &Custom, &Custom, div_assign;
     MulAssign, &Custom, &Custom, mul_assign;
     RemAssign, &Custom, &Custom, rem_assign;
+    ShlAssign, &Custom, &Custom, shl_assign;
+    ShrAssign, &Custom, &Custom, shr_assign;
     SubAssign, &Custom, &Custom, sub_assign;
 );
 
@@ -297,6 +313,10 @@ pub fn unknown_ops_or_runtime_ops_that_can_overflow() {
     _custom %= &Custom;
     _custom *= Custom;
     _custom *= &Custom;
+    _custom >>= Custom;
+    _custom >>= &Custom;
+    _custom <<= Custom;
+    _custom <<= &Custom;
     _custom += -Custom;
     _custom += &-Custom;
     _custom -= -Custom;
@@ -307,6 +327,10 @@ pub fn unknown_ops_or_runtime_ops_that_can_overflow() {
     _custom %= &-Custom;
     _custom *= -Custom;
     _custom *= &-Custom;
+    _custom >>= -Custom;
+    _custom >>= &-Custom;
+    _custom <<= -Custom;
+    _custom <<= &-Custom;
 
     // Binary
     _n = _n + 1;
@@ -347,6 +371,10 @@ pub fn unknown_ops_or_runtime_ops_that_can_overflow() {
     _custom = Custom + &Custom;
     _custom = &Custom + Custom;
     _custom = &Custom + &Custom;
+    _custom = _custom >> _custom;
+    _custom = _custom >> &_custom;
+    _custom = Custom << _custom;
+    _custom = &Custom << _custom;
 
     // Unary
     _n = -_n;
diff --git a/src/tools/clippy/tests/ui/arithmetic_side_effects.stderr b/src/tools/clippy/tests/ui/arithmetic_side_effects.stderr
index 17a2448fbfc..3895f08964c 100644
--- a/src/tools/clippy/tests/ui/arithmetic_side_effects.stderr
+++ b/src/tools/clippy/tests/ui/arithmetic_side_effects.stderr
@@ -1,5 +1,5 @@
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:270:5
+  --> $DIR/arithmetic_side_effects.rs:286:5
    |
 LL |     _n += 1;
    |     ^^^^^^^
@@ -7,592 +7,640 @@ LL |     _n += 1;
    = note: `-D clippy::arithmetic-side-effects` implied by `-D warnings`
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:271:5
+  --> $DIR/arithmetic_side_effects.rs:287:5
    |
 LL |     _n += &1;
    |     ^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:272:5
+  --> $DIR/arithmetic_side_effects.rs:288:5
    |
 LL |     _n -= 1;
    |     ^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:273:5
+  --> $DIR/arithmetic_side_effects.rs:289:5
    |
 LL |     _n -= &1;
    |     ^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:274:5
+  --> $DIR/arithmetic_side_effects.rs:290:5
    |
 LL |     _n /= 0;
    |     ^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:275:5
+  --> $DIR/arithmetic_side_effects.rs:291:5
    |
 LL |     _n /= &0;
    |     ^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:276:5
+  --> $DIR/arithmetic_side_effects.rs:292:5
    |
 LL |     _n %= 0;
    |     ^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:277:5
+  --> $DIR/arithmetic_side_effects.rs:293:5
    |
 LL |     _n %= &0;
    |     ^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:278:5
+  --> $DIR/arithmetic_side_effects.rs:294:5
    |
 LL |     _n *= 2;
    |     ^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:279:5
+  --> $DIR/arithmetic_side_effects.rs:295:5
    |
 LL |     _n *= &2;
    |     ^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:280:5
+  --> $DIR/arithmetic_side_effects.rs:296:5
    |
 LL |     _n += -1;
    |     ^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:281:5
+  --> $DIR/arithmetic_side_effects.rs:297:5
    |
 LL |     _n += &-1;
    |     ^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:282:5
+  --> $DIR/arithmetic_side_effects.rs:298:5
    |
 LL |     _n -= -1;
    |     ^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:283:5
+  --> $DIR/arithmetic_side_effects.rs:299:5
    |
 LL |     _n -= &-1;
    |     ^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:284:5
+  --> $DIR/arithmetic_side_effects.rs:300:5
    |
 LL |     _n /= -0;
    |     ^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:285:5
+  --> $DIR/arithmetic_side_effects.rs:301:5
    |
 LL |     _n /= &-0;
    |     ^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:286:5
+  --> $DIR/arithmetic_side_effects.rs:302:5
    |
 LL |     _n %= -0;
    |     ^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:287:5
+  --> $DIR/arithmetic_side_effects.rs:303:5
    |
 LL |     _n %= &-0;
    |     ^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:288:5
+  --> $DIR/arithmetic_side_effects.rs:304:5
    |
 LL |     _n *= -2;
    |     ^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:289:5
+  --> $DIR/arithmetic_side_effects.rs:305:5
    |
 LL |     _n *= &-2;
    |     ^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:290:5
+  --> $DIR/arithmetic_side_effects.rs:306:5
    |
 LL |     _custom += Custom;
    |     ^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:291:5
+  --> $DIR/arithmetic_side_effects.rs:307:5
    |
 LL |     _custom += &Custom;
    |     ^^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:292:5
+  --> $DIR/arithmetic_side_effects.rs:308:5
    |
 LL |     _custom -= Custom;
    |     ^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:293:5
+  --> $DIR/arithmetic_side_effects.rs:309:5
    |
 LL |     _custom -= &Custom;
    |     ^^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:294:5
+  --> $DIR/arithmetic_side_effects.rs:310:5
    |
 LL |     _custom /= Custom;
    |     ^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:295:5
+  --> $DIR/arithmetic_side_effects.rs:311:5
    |
 LL |     _custom /= &Custom;
    |     ^^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:296:5
+  --> $DIR/arithmetic_side_effects.rs:312:5
    |
 LL |     _custom %= Custom;
    |     ^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:297:5
+  --> $DIR/arithmetic_side_effects.rs:313:5
    |
 LL |     _custom %= &Custom;
    |     ^^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:298:5
+  --> $DIR/arithmetic_side_effects.rs:314:5
    |
 LL |     _custom *= Custom;
    |     ^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:299:5
+  --> $DIR/arithmetic_side_effects.rs:315:5
    |
 LL |     _custom *= &Custom;
    |     ^^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:300:5
+  --> $DIR/arithmetic_side_effects.rs:316:5
+   |
+LL |     _custom >>= Custom;
+   |     ^^^^^^^^^^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+  --> $DIR/arithmetic_side_effects.rs:317:5
+   |
+LL |     _custom >>= &Custom;
+   |     ^^^^^^^^^^^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+  --> $DIR/arithmetic_side_effects.rs:318:5
+   |
+LL |     _custom <<= Custom;
+   |     ^^^^^^^^^^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+  --> $DIR/arithmetic_side_effects.rs:319:5
+   |
+LL |     _custom <<= &Custom;
+   |     ^^^^^^^^^^^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+  --> $DIR/arithmetic_side_effects.rs:320:5
    |
 LL |     _custom += -Custom;
    |     ^^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:301:5
+  --> $DIR/arithmetic_side_effects.rs:321:5
    |
 LL |     _custom += &-Custom;
    |     ^^^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:302:5
+  --> $DIR/arithmetic_side_effects.rs:322:5
    |
 LL |     _custom -= -Custom;
    |     ^^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:303:5
+  --> $DIR/arithmetic_side_effects.rs:323:5
    |
 LL |     _custom -= &-Custom;
    |     ^^^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:304:5
+  --> $DIR/arithmetic_side_effects.rs:324:5
    |
 LL |     _custom /= -Custom;
    |     ^^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:305:5
+  --> $DIR/arithmetic_side_effects.rs:325:5
    |
 LL |     _custom /= &-Custom;
    |     ^^^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:306:5
+  --> $DIR/arithmetic_side_effects.rs:326:5
    |
 LL |     _custom %= -Custom;
    |     ^^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:307:5
+  --> $DIR/arithmetic_side_effects.rs:327:5
    |
 LL |     _custom %= &-Custom;
    |     ^^^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:308:5
+  --> $DIR/arithmetic_side_effects.rs:328:5
    |
 LL |     _custom *= -Custom;
    |     ^^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:309:5
+  --> $DIR/arithmetic_side_effects.rs:329:5
    |
 LL |     _custom *= &-Custom;
    |     ^^^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:312:10
+  --> $DIR/arithmetic_side_effects.rs:330:5
+   |
+LL |     _custom >>= -Custom;
+   |     ^^^^^^^^^^^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+  --> $DIR/arithmetic_side_effects.rs:331:5
+   |
+LL |     _custom >>= &-Custom;
+   |     ^^^^^^^^^^^^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+  --> $DIR/arithmetic_side_effects.rs:332:5
+   |
+LL |     _custom <<= -Custom;
+   |     ^^^^^^^^^^^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+  --> $DIR/arithmetic_side_effects.rs:333:5
+   |
+LL |     _custom <<= &-Custom;
+   |     ^^^^^^^^^^^^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+  --> $DIR/arithmetic_side_effects.rs:336:10
    |
 LL |     _n = _n + 1;
    |          ^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:313:10
+  --> $DIR/arithmetic_side_effects.rs:337:10
    |
 LL |     _n = _n + &1;
    |          ^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:314:10
+  --> $DIR/arithmetic_side_effects.rs:338:10
    |
 LL |     _n = 1 + _n;
    |          ^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:315:10
+  --> $DIR/arithmetic_side_effects.rs:339:10
    |
 LL |     _n = &1 + _n;
    |          ^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:316:10
+  --> $DIR/arithmetic_side_effects.rs:340:10
    |
 LL |     _n = _n - 1;
    |          ^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:317:10
+  --> $DIR/arithmetic_side_effects.rs:341:10
    |
 LL |     _n = _n - &1;
    |          ^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:318:10
+  --> $DIR/arithmetic_side_effects.rs:342:10
    |
 LL |     _n = 1 - _n;
    |          ^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:319:10
+  --> $DIR/arithmetic_side_effects.rs:343:10
    |
 LL |     _n = &1 - _n;
    |          ^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:320:10
+  --> $DIR/arithmetic_side_effects.rs:344:10
    |
 LL |     _n = _n / 0;
    |          ^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:321:10
+  --> $DIR/arithmetic_side_effects.rs:345:10
    |
 LL |     _n = _n / &0;
    |          ^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:322:10
+  --> $DIR/arithmetic_side_effects.rs:346:10
    |
 LL |     _n = _n % 0;
    |          ^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:323:10
+  --> $DIR/arithmetic_side_effects.rs:347:10
    |
 LL |     _n = _n % &0;
    |          ^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:324:10
+  --> $DIR/arithmetic_side_effects.rs:348:10
    |
 LL |     _n = _n * 2;
    |          ^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:325:10
+  --> $DIR/arithmetic_side_effects.rs:349:10
    |
 LL |     _n = _n * &2;
    |          ^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:326:10
+  --> $DIR/arithmetic_side_effects.rs:350:10
    |
 LL |     _n = 2 * _n;
    |          ^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:327:10
+  --> $DIR/arithmetic_side_effects.rs:351:10
    |
 LL |     _n = &2 * _n;
    |          ^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:328:10
+  --> $DIR/arithmetic_side_effects.rs:352:10
    |
 LL |     _n = 23 + &85;
    |          ^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:329:10
+  --> $DIR/arithmetic_side_effects.rs:353:10
    |
 LL |     _n = &23 + 85;
    |          ^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:330:10
+  --> $DIR/arithmetic_side_effects.rs:354:10
    |
 LL |     _n = &23 + &85;
    |          ^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:331:15
+  --> $DIR/arithmetic_side_effects.rs:355:15
    |
 LL |     _custom = _custom + _custom;
    |               ^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:332:15
+  --> $DIR/arithmetic_side_effects.rs:356:15
    |
 LL |     _custom = _custom + &_custom;
    |               ^^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:333:15
+  --> $DIR/arithmetic_side_effects.rs:357:15
    |
 LL |     _custom = Custom + _custom;
    |               ^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:334:15
+  --> $DIR/arithmetic_side_effects.rs:358:15
    |
 LL |     _custom = &Custom + _custom;
    |               ^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:335:15
+  --> $DIR/arithmetic_side_effects.rs:359:15
    |
 LL |     _custom = _custom - Custom;
    |               ^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:336:15
+  --> $DIR/arithmetic_side_effects.rs:360:15
    |
 LL |     _custom = _custom - &Custom;
    |               ^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:337:15
+  --> $DIR/arithmetic_side_effects.rs:361:15
    |
 LL |     _custom = Custom - _custom;
    |               ^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:338:15
+  --> $DIR/arithmetic_side_effects.rs:362:15
    |
 LL |     _custom = &Custom - _custom;
    |               ^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:339:15
+  --> $DIR/arithmetic_side_effects.rs:363:15
    |
 LL |     _custom = _custom / Custom;
    |               ^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:340:15
+  --> $DIR/arithmetic_side_effects.rs:364:15
    |
 LL |     _custom = _custom / &Custom;
    |               ^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:341:15
+  --> $DIR/arithmetic_side_effects.rs:365:15
    |
 LL |     _custom = _custom % Custom;
    |               ^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:342:15
+  --> $DIR/arithmetic_side_effects.rs:366:15
    |
 LL |     _custom = _custom % &Custom;
    |               ^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:343:15
+  --> $DIR/arithmetic_side_effects.rs:367:15
    |
 LL |     _custom = _custom * Custom;
    |               ^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:344:15
+  --> $DIR/arithmetic_side_effects.rs:368:15
    |
 LL |     _custom = _custom * &Custom;
    |               ^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:345:15
+  --> $DIR/arithmetic_side_effects.rs:369:15
    |
 LL |     _custom = Custom * _custom;
    |               ^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:346:15
+  --> $DIR/arithmetic_side_effects.rs:370:15
    |
 LL |     _custom = &Custom * _custom;
    |               ^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:347:15
+  --> $DIR/arithmetic_side_effects.rs:371:15
    |
 LL |     _custom = Custom + &Custom;
    |               ^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:348:15
+  --> $DIR/arithmetic_side_effects.rs:372:15
    |
 LL |     _custom = &Custom + Custom;
    |               ^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:349:15
+  --> $DIR/arithmetic_side_effects.rs:373:15
    |
 LL |     _custom = &Custom + &Custom;
    |               ^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:352:10
+  --> $DIR/arithmetic_side_effects.rs:374:15
+   |
+LL |     _custom = _custom >> _custom;
+   |               ^^^^^^^^^^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+  --> $DIR/arithmetic_side_effects.rs:375:15
+   |
+LL |     _custom = _custom >> &_custom;
+   |               ^^^^^^^^^^^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+  --> $DIR/arithmetic_side_effects.rs:376:15
+   |
+LL |     _custom = Custom << _custom;
+   |               ^^^^^^^^^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+  --> $DIR/arithmetic_side_effects.rs:377:15
+   |
+LL |     _custom = &Custom << _custom;
+   |               ^^^^^^^^^^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+  --> $DIR/arithmetic_side_effects.rs:380:10
    |
 LL |     _n = -_n;
    |          ^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:353:10
+  --> $DIR/arithmetic_side_effects.rs:381:10
    |
 LL |     _n = -&_n;
    |          ^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:354:15
+  --> $DIR/arithmetic_side_effects.rs:382:15
    |
 LL |     _custom = -_custom;
    |               ^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:355:15
+  --> $DIR/arithmetic_side_effects.rs:383:15
    |
 LL |     _custom = -&_custom;
    |               ^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:364:5
+  --> $DIR/arithmetic_side_effects.rs:392:5
    |
 LL |     1 + i;
    |     ^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:365:5
+  --> $DIR/arithmetic_side_effects.rs:393:5
    |
 LL |     i * 2;
    |     ^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:367:5
+  --> $DIR/arithmetic_side_effects.rs:395:5
    |
 LL |     i - 2 + 2 - i;
    |     ^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:368:5
+  --> $DIR/arithmetic_side_effects.rs:396:5
    |
 LL |     -i;
    |     ^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:369:5
-   |
-LL |     i >> 1;
-   |     ^^^^^^
-
-error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:370:5
-   |
-LL |     i << 1;
-   |     ^^^^^^
-
-error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:379:5
+  --> $DIR/arithmetic_side_effects.rs:407:5
    |
 LL |     i += 1;
    |     ^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:380:5
+  --> $DIR/arithmetic_side_effects.rs:408:5
    |
 LL |     i -= 1;
    |     ^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:381:5
+  --> $DIR/arithmetic_side_effects.rs:409:5
    |
 LL |     i *= 2;
    |     ^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:383:5
+  --> $DIR/arithmetic_side_effects.rs:411:5
    |
 LL |     i /= 0;
    |     ^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:385:5
+  --> $DIR/arithmetic_side_effects.rs:413:5
    |
 LL |     i /= var1;
    |     ^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:386:5
+  --> $DIR/arithmetic_side_effects.rs:414:5
    |
 LL |     i /= var2;
    |     ^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:388:5
+  --> $DIR/arithmetic_side_effects.rs:416:5
    |
 LL |     i %= 0;
    |     ^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:390:5
+  --> $DIR/arithmetic_side_effects.rs:418:5
    |
 LL |     i %= var1;
    |     ^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:391:5
+  --> $DIR/arithmetic_side_effects.rs:419:5
    |
 LL |     i %= var2;
    |     ^^^^^^^^^
 
-error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:392:5
-   |
-LL |     i <<= 3;
-   |     ^^^^^^^
-
-error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:393:5
-   |
-LL |     i >>= 2;
-   |     ^^^^^^^
-
-error: aborting due to 99 previous errors
+error: aborting due to 107 previous errors
 
diff --git a/src/tools/clippy/tests/ui/async_yields_async.fixed b/src/tools/clippy/tests/ui/async_yields_async.fixed
index 3cf380d2b95..579a63ea477 100644
--- a/src/tools/clippy/tests/ui/async_yields_async.fixed
+++ b/src/tools/clippy/tests/ui/async_yields_async.fixed
@@ -2,6 +2,7 @@
 #![feature(lint_reasons)]
 #![feature(async_closure)]
 #![warn(clippy::async_yields_async)]
+#![allow(clippy::redundant_async_block)]
 
 use core::future::Future;
 use core::pin::Pin;
diff --git a/src/tools/clippy/tests/ui/async_yields_async.rs b/src/tools/clippy/tests/ui/async_yields_async.rs
index dd4131b60ab..5aec2fb50f6 100644
--- a/src/tools/clippy/tests/ui/async_yields_async.rs
+++ b/src/tools/clippy/tests/ui/async_yields_async.rs
@@ -2,6 +2,7 @@
 #![feature(lint_reasons)]
 #![feature(async_closure)]
 #![warn(clippy::async_yields_async)]
+#![allow(clippy::redundant_async_block)]
 
 use core::future::Future;
 use core::pin::Pin;
diff --git a/src/tools/clippy/tests/ui/async_yields_async.stderr b/src/tools/clippy/tests/ui/async_yields_async.stderr
index 22ce1c6f647..7f72534832b 100644
--- a/src/tools/clippy/tests/ui/async_yields_async.stderr
+++ b/src/tools/clippy/tests/ui/async_yields_async.stderr
@@ -1,5 +1,5 @@
 error: an async construct yields a type which is itself awaitable
-  --> $DIR/async_yields_async.rs:39:9
+  --> $DIR/async_yields_async.rs:40:9
    |
 LL |        let _h = async {
    |  _____________________-
@@ -19,7 +19,7 @@ LL +         }.await
    |
 
 error: an async construct yields a type which is itself awaitable
-  --> $DIR/async_yields_async.rs:44:9
+  --> $DIR/async_yields_async.rs:45:9
    |
 LL |       let _i = async {
    |  ____________________-
@@ -32,7 +32,7 @@ LL | |     };
    | |_____- outer async construct
 
 error: an async construct yields a type which is itself awaitable
-  --> $DIR/async_yields_async.rs:50:9
+  --> $DIR/async_yields_async.rs:51:9
    |
 LL |        let _j = async || {
    |  ________________________-
@@ -51,7 +51,7 @@ LL +         }.await
    |
 
 error: an async construct yields a type which is itself awaitable
-  --> $DIR/async_yields_async.rs:55:9
+  --> $DIR/async_yields_async.rs:56:9
    |
 LL |       let _k = async || {
    |  _______________________-
@@ -64,7 +64,7 @@ LL | |     };
    | |_____- outer async construct
 
 error: an async construct yields a type which is itself awaitable
-  --> $DIR/async_yields_async.rs:57:23
+  --> $DIR/async_yields_async.rs:58:23
    |
 LL |     let _l = async || CustomFutureType;
    |                       ^^^^^^^^^^^^^^^^
@@ -74,7 +74,7 @@ LL |     let _l = async || CustomFutureType;
    |                       help: consider awaiting this value: `CustomFutureType.await`
 
 error: an async construct yields a type which is itself awaitable
-  --> $DIR/async_yields_async.rs:63:9
+  --> $DIR/async_yields_async.rs:64:9
    |
 LL |       let _m = async || {
    |  _______________________-
diff --git a/src/tools/clippy/tests/ui/author/blocks.stdout b/src/tools/clippy/tests/ui/author/blocks.stdout
index c6acf24c21e..eb3e5189c82 100644
--- a/src/tools/clippy/tests/ui/author/blocks.stdout
+++ b/src/tools/clippy/tests/ui/author/blocks.stdout
@@ -43,11 +43,7 @@ if let ExprKind::Block(block, None) = expr.kind
 if let ExprKind::Closure(CaptureBy::Value, fn_decl, body_id, _, None) = expr.kind
     && let FnRetTy::DefaultReturn(_) = fn_decl.output
     && expr1 = &cx.tcx.hir().body(body_id).value
-    && let ExprKind::Call(func, args) = expr1.kind
-    && let ExprKind::Path(ref qpath) = func.kind
-    && matches!(qpath, QPath::LangItem(LangItem::IdentityFuture, _))
-    && args.len() == 1
-    && let ExprKind::Closure(CaptureBy::Value, fn_decl1, body_id1, _, Some(Movability::Static)) = args[0].kind
+    && let ExprKind::Closure(CaptureBy::Value, fn_decl1, body_id1, _, Some(Movability::Static)) = expr1.kind
     && let FnRetTy::DefaultReturn(_) = fn_decl1.output
     && expr2 = &cx.tcx.hir().body(body_id1).value
     && let ExprKind::Block(block, None) = expr2.kind
diff --git a/src/tools/clippy/tests/ui/boxed_local.rs b/src/tools/clippy/tests/ui/boxed_local.rs
index 4639f00a8d8..79b6d33fc77 100644
--- a/src/tools/clippy/tests/ui/boxed_local.rs
+++ b/src/tools/clippy/tests/ui/boxed_local.rs
@@ -1,4 +1,3 @@
-#![feature(box_syntax)]
 #![feature(lint_reasons)]
 #![allow(
     clippy::borrowed_box,
@@ -34,7 +33,7 @@ fn ok_box_trait(boxed_trait: &Box<dyn Z>) {
 }
 
 fn warn_call() {
-    let x = box A;
+    let x = Box::new(A);
     x.foo();
 }
 
@@ -43,41 +42,41 @@ fn warn_arg(x: Box<A>) {
 }
 
 fn nowarn_closure_arg() {
-    let x = Some(box A);
+    let x = Some(Box::new(A));
     x.map_or((), |x| take_ref(&x));
 }
 
 fn warn_rename_call() {
-    let x = box A;
+    let x = Box::new(A);
 
     let y = x;
     y.foo(); // via autoderef
 }
 
 fn warn_notuse() {
-    let bz = box A;
+    let bz = Box::new(A);
 }
 
 fn warn_pass() {
-    let bz = box A;
+    let bz = Box::new(A);
     take_ref(&bz); // via deref coercion
 }
 
 fn nowarn_return() -> Box<A> {
-    box A // moved out, "escapes"
+    Box::new(A) // moved out, "escapes"
 }
 
 fn nowarn_move() {
-    let bx = box A;
+    let bx = Box::new(A);
     drop(bx) // moved in, "escapes"
 }
 fn nowarn_call() {
-    let bx = box A;
+    let bx = Box::new(A);
     bx.clone(); // method only available to Box, not via autoderef
 }
 
 fn nowarn_pass() {
-    let bx = box A;
+    let bx = Box::new(A);
     take_box(&bx); // fn needs &Box
 }
 
@@ -86,30 +85,20 @@ fn take_ref(x: &A) {}
 
 fn nowarn_ref_take() {
     // false positive, should actually warn
-    let x = box A;
+    let x = Box::new(A);
     let y = &x;
     take_box(y);
 }
 
 fn nowarn_match() {
-    let x = box A; // moved into a match
+    let x = Box::new(A); // moved into a match
     match x {
         y => drop(y),
     }
 }
 
 fn warn_match() {
-    let x = box A;
-    match &x {
-        // not moved
-        y => (),
-    }
-}
-
-fn nowarn_large_array() {
-    // should not warn, is large array
-    // and should not be on stack
-    let x = box [1; 10000];
+    let x = Box::new(A);
     match &x {
         // not moved
         y => (),
diff --git a/src/tools/clippy/tests/ui/boxed_local.stderr b/src/tools/clippy/tests/ui/boxed_local.stderr
index 9036529f39c..10d78fbc0ab 100644
--- a/src/tools/clippy/tests/ui/boxed_local.stderr
+++ b/src/tools/clippy/tests/ui/boxed_local.stderr
@@ -1,5 +1,5 @@
 error: local variable doesn't need to be boxed here
-  --> $DIR/boxed_local.rs:41:13
+  --> $DIR/boxed_local.rs:40:13
    |
 LL | fn warn_arg(x: Box<A>) {
    |             ^
@@ -7,19 +7,19 @@ LL | fn warn_arg(x: Box<A>) {
    = note: `-D clippy::boxed-local` implied by `-D warnings`
 
 error: local variable doesn't need to be boxed here
-  --> $DIR/boxed_local.rs:132:12
+  --> $DIR/boxed_local.rs:121:12
    |
 LL | pub fn new(_needs_name: Box<PeekableSeekable<&()>>) -> () {}
    |            ^^^^^^^^^^^
 
 error: local variable doesn't need to be boxed here
-  --> $DIR/boxed_local.rs:196:44
+  --> $DIR/boxed_local.rs:185:44
    |
 LL |         fn default_impl_x(self: Box<Self>, x: Box<u32>) -> u32 {
    |                                            ^
 
 error: local variable doesn't need to be boxed here
-  --> $DIR/boxed_local.rs:203:16
+  --> $DIR/boxed_local.rs:192:16
    |
 LL |         fn foo(x: Box<u32>) {}
    |                ^
diff --git a/src/tools/clippy/tests/ui/collection_is_never_read.rs b/src/tools/clippy/tests/ui/collection_is_never_read.rs
new file mode 100644
index 00000000000..068a49486cf
--- /dev/null
+++ b/src/tools/clippy/tests/ui/collection_is_never_read.rs
@@ -0,0 +1,165 @@
+#![allow(unused)]
+#![warn(clippy::collection_is_never_read)]
+
+use std::collections::{HashMap, HashSet};
+
+fn main() {}
+
+fn not_a_collection() {
+    // TODO: Expand `collection_is_never_read` beyond collections?
+    let mut x = 10; // Ok
+    x += 1;
+}
+
+fn no_access_at_all() {
+    // Other lints should catch this.
+    let x = vec![1, 2, 3]; // Ok
+}
+
+fn write_without_read() {
+    // The main use case for `collection_is_never_read`.
+    let mut x = HashMap::new(); // WARNING
+    x.insert(1, 2);
+}
+
+fn read_without_write() {
+    let mut x = vec![1, 2, 3]; // Ok
+    let _ = x.len();
+}
+
+fn write_and_read() {
+    let mut x = vec![1, 2, 3]; // Ok
+    x.push(4);
+    let _ = x.len();
+}
+
+fn write_after_read() {
+    // TODO: Warn here, but this requires more extensive data flow analysis.
+    let mut x = vec![1, 2, 3]; // Ok
+    let _ = x.len();
+    x.push(4); // Pointless
+}
+
+fn write_before_reassign() {
+    // TODO: Warn here, but this requires more extensive data flow analysis.
+    let mut x = HashMap::new(); // Ok
+    x.insert(1, 2); // Pointless
+    x = HashMap::new();
+    let _ = x.len();
+}
+
+fn read_in_closure() {
+    let mut x = HashMap::new(); // Ok
+    x.insert(1, 2);
+    let _ = || {
+        let _ = x.len();
+    };
+}
+
+fn write_in_closure() {
+    let mut x = vec![1, 2, 3]; // WARNING
+    let _ = || {
+        x.push(4);
+    };
+}
+
+fn read_in_format() {
+    let mut x = HashMap::new(); // Ok
+    x.insert(1, 2);
+    format!("{x:?}");
+}
+
+fn shadowing_1() {
+    let x = HashMap::<usize, usize>::new(); // Ok
+    let _ = x.len();
+    let mut x = HashMap::new(); // WARNING
+    x.insert(1, 2);
+}
+
+fn shadowing_2() {
+    let mut x = HashMap::new(); // WARNING
+    x.insert(1, 2);
+    let x = HashMap::<usize, usize>::new(); // Ok
+    let _ = x.len();
+}
+
+#[allow(clippy::let_unit_value)]
+fn fake_read() {
+    let mut x = vec![1, 2, 3]; // Ok
+    x.reverse();
+    // `collection_is_never_read` gets fooled, but other lints should catch this.
+    let _: () = x.clear();
+}
+
+fn assignment() {
+    let mut x = vec![1, 2, 3]; // WARNING
+    let y = vec![4, 5, 6]; // Ok
+    x = y;
+}
+
+#[allow(clippy::self_assignment)]
+fn self_assignment() {
+    let mut x = vec![1, 2, 3]; // WARNING
+    x = x;
+}
+
+fn method_argument_but_not_target() {
+    struct MyStruct;
+    impl MyStruct {
+        fn my_method(&self, _argument: &[usize]) {}
+    }
+    let my_struct = MyStruct;
+
+    let mut x = vec![1, 2, 3]; // Ok
+    x.reverse();
+    my_struct.my_method(&x);
+}
+
+fn insert_is_not_a_read() {
+    let mut x = HashSet::new(); // WARNING
+    x.insert(5);
+}
+
+fn insert_is_a_read() {
+    let mut x = HashSet::new(); // Ok
+    if x.insert(5) {
+        println!("5 was inserted");
+    }
+}
+
+fn not_read_if_return_value_not_used() {
+    // `is_empty` does not modify the set, so it's a query. But since the return value is not used, the
+    // lint does not consider it a read here.
+    let x = vec![1, 2, 3]; // WARNING
+    x.is_empty();
+}
+
+fn extension_traits() {
+    trait VecExt<T> {
+        fn method_with_side_effect(&self);
+        fn method_without_side_effect(&self);
+    }
+
+    impl<T> VecExt<T> for Vec<T> {
+        fn method_with_side_effect(&self) {
+            println!("my length: {}", self.len());
+        }
+        fn method_without_side_effect(&self) {}
+    }
+
+    let x = vec![1, 2, 3]; // Ok
+    x.method_with_side_effect();
+
+    let y = vec![1, 2, 3]; // Ok (false negative)
+    y.method_without_side_effect();
+}
+
+fn function_argument() {
+    #[allow(clippy::ptr_arg)]
+    fn foo<T>(v: &Vec<T>) -> usize {
+        v.len()
+    }
+
+    let x = vec![1, 2, 3]; // Ok
+    foo(&x);
+}
diff --git a/src/tools/clippy/tests/ui/collection_is_never_read.stderr b/src/tools/clippy/tests/ui/collection_is_never_read.stderr
new file mode 100644
index 00000000000..7654b74be3d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/collection_is_never_read.stderr
@@ -0,0 +1,52 @@
+error: collection is never read
+  --> $DIR/collection_is_never_read.rs:21:5
+   |
+LL |     let mut x = HashMap::new(); // WARNING
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `-D clippy::collection-is-never-read` implied by `-D warnings`
+
+error: collection is never read
+  --> $DIR/collection_is_never_read.rs:60:5
+   |
+LL |     let mut x = vec![1, 2, 3]; // WARNING
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: collection is never read
+  --> $DIR/collection_is_never_read.rs:75:5
+   |
+LL |     let mut x = HashMap::new(); // WARNING
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: collection is never read
+  --> $DIR/collection_is_never_read.rs:80:5
+   |
+LL |     let mut x = HashMap::new(); // WARNING
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: collection is never read
+  --> $DIR/collection_is_never_read.rs:95:5
+   |
+LL |     let mut x = vec![1, 2, 3]; // WARNING
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: collection is never read
+  --> $DIR/collection_is_never_read.rs:102:5
+   |
+LL |     let mut x = vec![1, 2, 3]; // WARNING
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: collection is never read
+  --> $DIR/collection_is_never_read.rs:119:5
+   |
+LL |     let mut x = HashSet::new(); // WARNING
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: collection is never read
+  --> $DIR/collection_is_never_read.rs:133:5
+   |
+LL |     let x = vec![1, 2, 3]; // WARNING
+   |     ^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 8 previous errors
+
diff --git a/src/tools/clippy/tests/ui/crashes/ice-10148.rs b/src/tools/clippy/tests/ui/crashes/ice-10148.rs
new file mode 100644
index 00000000000..af33b10c693
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-10148.rs
@@ -0,0 +1,9 @@
+// aux-build:../../auxiliary/proc_macro_with_span.rs
+
+extern crate proc_macro_with_span;
+
+use proc_macro_with_span::with_span;
+
+fn main() {
+    println!(with_span!(""something ""));
+}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-10148.stderr b/src/tools/clippy/tests/ui/crashes/ice-10148.stderr
new file mode 100644
index 00000000000..f23e4433f95
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-10148.stderr
@@ -0,0 +1,12 @@
+error: empty string literal in `println!`
+  --> $DIR/ice-10148.rs:8:5
+   |
+LL |     println!(with_span!(""something ""));
+   |     ^^^^^^^^^^^^^^^^^^^^-----------^^^^^
+   |                         |
+   |                         help: remove the empty string
+   |
+   = note: `-D clippy::println-empty-string` implied by `-D warnings`
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/crashes/ice-6179.rs b/src/tools/clippy/tests/ui/crashes/ice-6179.rs
index 4fe92d356c4..ce1895851e2 100644
--- a/src/tools/clippy/tests/ui/crashes/ice-6179.rs
+++ b/src/tools/clippy/tests/ui/crashes/ice-6179.rs
@@ -2,7 +2,7 @@
 //! The ICE is mainly caused by using `hir_ty_to_ty`. See the discussion in the PR for details.
 
 #![warn(clippy::use_self)]
-#![allow(dead_code)]
+#![allow(dead_code, clippy::let_with_type_underscore)]
 
 struct Foo;
 
diff --git a/src/tools/clippy/tests/ui/crashes/ice-rust-107877.rs b/src/tools/clippy/tests/ui/crashes/ice-rust-107877.rs
new file mode 100644
index 00000000000..7f5bae60d55
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-rust-107877.rs
@@ -0,0 +1,17 @@
+#![allow(dead_code)]
+
+struct Foo;
+
+impl<'a> std::convert::TryFrom<&'a String> for Foo {
+    type Error = std::convert::Infallible;
+
+    fn try_from(_: &'a String) -> Result<Self, Self::Error> {
+        Ok(Foo)
+    }
+}
+
+fn find<E>(_: impl std::convert::TryInto<Foo, Error = E>) {}
+
+fn main() {
+    find(&String::new());
+}
diff --git a/src/tools/clippy/tests/ui/default_numeric_fallback_f64.fixed b/src/tools/clippy/tests/ui/default_numeric_fallback_f64.fixed
index a370ccc7696..a9e5fd159af 100644
--- a/src/tools/clippy/tests/ui/default_numeric_fallback_f64.fixed
+++ b/src/tools/clippy/tests/ui/default_numeric_fallback_f64.fixed
@@ -9,7 +9,8 @@
     clippy::unnecessary_operation,
     clippy::branches_sharing_code,
     clippy::match_single_binding,
-    clippy::let_unit_value
+    clippy::let_unit_value,
+    clippy::let_with_type_underscore
 )]
 
 #[macro_use]
diff --git a/src/tools/clippy/tests/ui/default_numeric_fallback_f64.rs b/src/tools/clippy/tests/ui/default_numeric_fallback_f64.rs
index 2476fe95141..085f8f452b2 100644
--- a/src/tools/clippy/tests/ui/default_numeric_fallback_f64.rs
+++ b/src/tools/clippy/tests/ui/default_numeric_fallback_f64.rs
@@ -9,7 +9,8 @@
     clippy::unnecessary_operation,
     clippy::branches_sharing_code,
     clippy::match_single_binding,
-    clippy::let_unit_value
+    clippy::let_unit_value,
+    clippy::let_with_type_underscore
 )]
 
 #[macro_use]
diff --git a/src/tools/clippy/tests/ui/default_numeric_fallback_f64.stderr b/src/tools/clippy/tests/ui/default_numeric_fallback_f64.stderr
index 5df2f642388..44c6f1a9bea 100644
--- a/src/tools/clippy/tests/ui/default_numeric_fallback_f64.stderr
+++ b/src/tools/clippy/tests/ui/default_numeric_fallback_f64.stderr
@@ -1,5 +1,5 @@
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:21:17
+  --> $DIR/default_numeric_fallback_f64.rs:22:17
    |
 LL |         let x = 0.12;
    |                 ^^^^ help: consider adding suffix: `0.12_f64`
@@ -7,139 +7,139 @@ LL |         let x = 0.12;
    = note: `-D clippy::default-numeric-fallback` implied by `-D warnings`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:22:18
+  --> $DIR/default_numeric_fallback_f64.rs:23:18
    |
 LL |         let x = [1., 2., 3.];
    |                  ^^ help: consider adding suffix: `1.0_f64`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:22:22
+  --> $DIR/default_numeric_fallback_f64.rs:23:22
    |
 LL |         let x = [1., 2., 3.];
    |                      ^^ help: consider adding suffix: `2.0_f64`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:22:26
+  --> $DIR/default_numeric_fallback_f64.rs:23:26
    |
 LL |         let x = [1., 2., 3.];
    |                          ^^ help: consider adding suffix: `3.0_f64`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:23:28
+  --> $DIR/default_numeric_fallback_f64.rs:24:28
    |
 LL |         let x = if true { (1., 2.) } else { (3., 4.) };
    |                            ^^ help: consider adding suffix: `1.0_f64`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:23:32
+  --> $DIR/default_numeric_fallback_f64.rs:24:32
    |
 LL |         let x = if true { (1., 2.) } else { (3., 4.) };
    |                                ^^ help: consider adding suffix: `2.0_f64`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:23:46
+  --> $DIR/default_numeric_fallback_f64.rs:24:46
    |
 LL |         let x = if true { (1., 2.) } else { (3., 4.) };
    |                                              ^^ help: consider adding suffix: `3.0_f64`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:23:50
+  --> $DIR/default_numeric_fallback_f64.rs:24:50
    |
 LL |         let x = if true { (1., 2.) } else { (3., 4.) };
    |                                                  ^^ help: consider adding suffix: `4.0_f64`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:24:23
+  --> $DIR/default_numeric_fallback_f64.rs:25:23
    |
 LL |         let x = match 1. {
    |                       ^^ help: consider adding suffix: `1.0_f64`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:25:18
+  --> $DIR/default_numeric_fallback_f64.rs:26:18
    |
 LL |             _ => 1.,
    |                  ^^ help: consider adding suffix: `1.0_f64`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:44:21
+  --> $DIR/default_numeric_fallback_f64.rs:45:21
    |
 LL |             let y = 1.;
    |                     ^^ help: consider adding suffix: `1.0_f64`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:52:21
+  --> $DIR/default_numeric_fallback_f64.rs:53:21
    |
 LL |             let y = 1.;
    |                     ^^ help: consider adding suffix: `1.0_f64`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:58:21
+  --> $DIR/default_numeric_fallback_f64.rs:59:21
    |
 LL |             let y = 1.;
    |                     ^^ help: consider adding suffix: `1.0_f64`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:66:21
+  --> $DIR/default_numeric_fallback_f64.rs:67:21
    |
 LL |             let y = 1.;
    |                     ^^ help: consider adding suffix: `1.0_f64`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:78:9
+  --> $DIR/default_numeric_fallback_f64.rs:79:9
    |
 LL |         1.
    |         ^^ help: consider adding suffix: `1.0_f64`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:84:27
+  --> $DIR/default_numeric_fallback_f64.rs:85:27
    |
 LL |         let f = || -> _ { 1. };
    |                           ^^ help: consider adding suffix: `1.0_f64`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:88:29
+  --> $DIR/default_numeric_fallback_f64.rs:89:29
    |
 LL |         let f = || -> f64 { 1. };
    |                             ^^ help: consider adding suffix: `1.0_f64`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:102:21
+  --> $DIR/default_numeric_fallback_f64.rs:103:21
    |
 LL |         generic_arg(1.);
    |                     ^^ help: consider adding suffix: `1.0_f64`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:105:32
+  --> $DIR/default_numeric_fallback_f64.rs:106:32
    |
 LL |         let x: _ = generic_arg(1.);
    |                                ^^ help: consider adding suffix: `1.0_f64`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:123:28
+  --> $DIR/default_numeric_fallback_f64.rs:124:28
    |
 LL |         GenericStruct { x: 1. };
    |                            ^^ help: consider adding suffix: `1.0_f64`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:126:36
+  --> $DIR/default_numeric_fallback_f64.rs:127:36
    |
 LL |         let _ = GenericStruct { x: 1. };
    |                                    ^^ help: consider adding suffix: `1.0_f64`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:144:24
+  --> $DIR/default_numeric_fallback_f64.rs:145:24
    |
 LL |         GenericEnum::X(1.);
    |                        ^^ help: consider adding suffix: `1.0_f64`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:164:23
+  --> $DIR/default_numeric_fallback_f64.rs:165:23
    |
 LL |         s.generic_arg(1.);
    |                       ^^ help: consider adding suffix: `1.0_f64`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:171:21
+  --> $DIR/default_numeric_fallback_f64.rs:172:21
    |
 LL |             let x = 22.;
    |                     ^^^ help: consider adding suffix: `22.0_f64`
diff --git a/src/tools/clippy/tests/ui/default_numeric_fallback_i32.fixed b/src/tools/clippy/tests/ui/default_numeric_fallback_i32.fixed
index 3f4994f0453..63ac4d5aeb6 100644
--- a/src/tools/clippy/tests/ui/default_numeric_fallback_i32.fixed
+++ b/src/tools/clippy/tests/ui/default_numeric_fallback_i32.fixed
@@ -9,7 +9,8 @@
     clippy::no_effect,
     clippy::unnecessary_operation,
     clippy::branches_sharing_code,
-    clippy::let_unit_value
+    clippy::let_unit_value,
+    clippy::let_with_type_underscore
 )]
 
 #[macro_use]
diff --git a/src/tools/clippy/tests/ui/default_numeric_fallback_i32.rs b/src/tools/clippy/tests/ui/default_numeric_fallback_i32.rs
index 2df0e09787f..28e6eceb80e 100644
--- a/src/tools/clippy/tests/ui/default_numeric_fallback_i32.rs
+++ b/src/tools/clippy/tests/ui/default_numeric_fallback_i32.rs
@@ -9,7 +9,8 @@
     clippy::no_effect,
     clippy::unnecessary_operation,
     clippy::branches_sharing_code,
-    clippy::let_unit_value
+    clippy::let_unit_value,
+    clippy::let_with_type_underscore
 )]
 
 #[macro_use]
diff --git a/src/tools/clippy/tests/ui/default_numeric_fallback_i32.stderr b/src/tools/clippy/tests/ui/default_numeric_fallback_i32.stderr
index 6f219c3fc2b..dd91574d5b3 100644
--- a/src/tools/clippy/tests/ui/default_numeric_fallback_i32.stderr
+++ b/src/tools/clippy/tests/ui/default_numeric_fallback_i32.stderr
@@ -1,5 +1,5 @@
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:21:17
+  --> $DIR/default_numeric_fallback_i32.rs:22:17
    |
 LL |         let x = 22;
    |                 ^^ help: consider adding suffix: `22_i32`
@@ -7,151 +7,151 @@ LL |         let x = 22;
    = note: `-D clippy::default-numeric-fallback` implied by `-D warnings`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:22:18
+  --> $DIR/default_numeric_fallback_i32.rs:23:18
    |
 LL |         let x = [1, 2, 3];
    |                  ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:22:21
+  --> $DIR/default_numeric_fallback_i32.rs:23:21
    |
 LL |         let x = [1, 2, 3];
    |                     ^ help: consider adding suffix: `2_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:22:24
+  --> $DIR/default_numeric_fallback_i32.rs:23:24
    |
 LL |         let x = [1, 2, 3];
    |                        ^ help: consider adding suffix: `3_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:23:28
+  --> $DIR/default_numeric_fallback_i32.rs:24:28
    |
 LL |         let x = if true { (1, 2) } else { (3, 4) };
    |                            ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:23:31
+  --> $DIR/default_numeric_fallback_i32.rs:24:31
    |
 LL |         let x = if true { (1, 2) } else { (3, 4) };
    |                               ^ help: consider adding suffix: `2_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:23:44
+  --> $DIR/default_numeric_fallback_i32.rs:24:44
    |
 LL |         let x = if true { (1, 2) } else { (3, 4) };
    |                                            ^ help: consider adding suffix: `3_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:23:47
+  --> $DIR/default_numeric_fallback_i32.rs:24:47
    |
 LL |         let x = if true { (1, 2) } else { (3, 4) };
    |                                               ^ help: consider adding suffix: `4_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:24:23
+  --> $DIR/default_numeric_fallback_i32.rs:25:23
    |
 LL |         let x = match 1 {
    |                       ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:25:13
+  --> $DIR/default_numeric_fallback_i32.rs:26:13
    |
 LL |             1 => 1,
    |             ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:25:18
+  --> $DIR/default_numeric_fallback_i32.rs:26:18
    |
 LL |             1 => 1,
    |                  ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:26:18
+  --> $DIR/default_numeric_fallback_i32.rs:27:18
    |
 LL |             _ => 2,
    |                  ^ help: consider adding suffix: `2_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:45:21
+  --> $DIR/default_numeric_fallback_i32.rs:46:21
    |
 LL |             let y = 1;
    |                     ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:53:21
+  --> $DIR/default_numeric_fallback_i32.rs:54:21
    |
 LL |             let y = 1;
    |                     ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:59:21
+  --> $DIR/default_numeric_fallback_i32.rs:60:21
    |
 LL |             let y = 1;
    |                     ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:67:21
+  --> $DIR/default_numeric_fallback_i32.rs:68:21
    |
 LL |             let y = 1;
    |                     ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:79:9
+  --> $DIR/default_numeric_fallback_i32.rs:80:9
    |
 LL |         1
    |         ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:85:27
+  --> $DIR/default_numeric_fallback_i32.rs:86:27
    |
 LL |         let f = || -> _ { 1 };
    |                           ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:89:29
+  --> $DIR/default_numeric_fallback_i32.rs:90:29
    |
 LL |         let f = || -> i32 { 1 };
    |                             ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:103:21
+  --> $DIR/default_numeric_fallback_i32.rs:104:21
    |
 LL |         generic_arg(1);
    |                     ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:106:32
+  --> $DIR/default_numeric_fallback_i32.rs:107:32
    |
 LL |         let x: _ = generic_arg(1);
    |                                ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:124:28
+  --> $DIR/default_numeric_fallback_i32.rs:125:28
    |
 LL |         GenericStruct { x: 1 };
    |                            ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:127:36
+  --> $DIR/default_numeric_fallback_i32.rs:128:36
    |
 LL |         let _ = GenericStruct { x: 1 };
    |                                    ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:145:24
+  --> $DIR/default_numeric_fallback_i32.rs:146:24
    |
 LL |         GenericEnum::X(1);
    |                        ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:165:23
+  --> $DIR/default_numeric_fallback_i32.rs:166:23
    |
 LL |         s.generic_arg(1);
    |                       ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:172:21
+  --> $DIR/default_numeric_fallback_i32.rs:173:21
    |
 LL |             let x = 22;
    |                     ^^ help: consider adding suffix: `22_i32`
diff --git a/src/tools/clippy/tests/ui/derivable_impls.fixed b/src/tools/clippy/tests/ui/derivable_impls.fixed
index ee8456f5deb..89ec33a0d8f 100644
--- a/src/tools/clippy/tests/ui/derivable_impls.fixed
+++ b/src/tools/clippy/tests/ui/derivable_impls.fixed
@@ -231,4 +231,41 @@ impl Default for NonExhaustiveEnum {
     }
 }
 
+// https://github.com/rust-lang/rust-clippy/issues/10396
+
+#[derive(Default)]
+struct DefaultType;
+
+struct GenericType<T = DefaultType> {
+    t: T,
+}
+
+impl Default for GenericType {
+    fn default() -> Self {
+        Self { t: Default::default() }
+    }
+}
+
+struct InnerGenericType<T> {
+    t: T,
+}
+
+impl Default for InnerGenericType<DefaultType> {
+    fn default() -> Self {
+        Self { t: Default::default() }
+    }
+}
+
+struct OtherGenericType<T = DefaultType> {
+    inner: InnerGenericType<T>,
+}
+
+impl Default for OtherGenericType {
+    fn default() -> Self {
+        Self {
+            inner: Default::default(),
+        }
+    }
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/derivable_impls.rs b/src/tools/clippy/tests/ui/derivable_impls.rs
index 14af419bcad..def6e41162f 100644
--- a/src/tools/clippy/tests/ui/derivable_impls.rs
+++ b/src/tools/clippy/tests/ui/derivable_impls.rs
@@ -267,4 +267,41 @@ impl Default for NonExhaustiveEnum {
     }
 }
 
+// https://github.com/rust-lang/rust-clippy/issues/10396
+
+#[derive(Default)]
+struct DefaultType;
+
+struct GenericType<T = DefaultType> {
+    t: T,
+}
+
+impl Default for GenericType {
+    fn default() -> Self {
+        Self { t: Default::default() }
+    }
+}
+
+struct InnerGenericType<T> {
+    t: T,
+}
+
+impl Default for InnerGenericType<DefaultType> {
+    fn default() -> Self {
+        Self { t: Default::default() }
+    }
+}
+
+struct OtherGenericType<T = DefaultType> {
+    inner: InnerGenericType<T>,
+}
+
+impl Default for OtherGenericType {
+    fn default() -> Self {
+        Self {
+            inner: Default::default(),
+        }
+    }
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/erasing_op.rs b/src/tools/clippy/tests/ui/erasing_op.rs
index ae2fad0086d..74985029e00 100644
--- a/src/tools/clippy/tests/ui/erasing_op.rs
+++ b/src/tools/clippy/tests/ui/erasing_op.rs
@@ -31,9 +31,7 @@ impl core::ops::Mul<i32> for Vec1 {
 
 #[allow(clippy::no_effect)]
 #[warn(clippy::erasing_op)]
-fn main() {
-    let x: u8 = 0;
-
+fn test(x: u8) {
     x * 0;
     0 & x;
     0 / x;
@@ -41,3 +39,7 @@ fn main() {
     0 * Vec1 { x: 5 };
     Vec1 { x: 5 } * 0;
 }
+
+fn main() {
+    test(0)
+}
diff --git a/src/tools/clippy/tests/ui/erasing_op.stderr b/src/tools/clippy/tests/ui/erasing_op.stderr
index 165ed9bfe58..97941252355 100644
--- a/src/tools/clippy/tests/ui/erasing_op.stderr
+++ b/src/tools/clippy/tests/ui/erasing_op.stderr
@@ -1,5 +1,5 @@
 error: this operation will always return zero. This is likely not the intended outcome
-  --> $DIR/erasing_op.rs:37:5
+  --> $DIR/erasing_op.rs:35:5
    |
 LL |     x * 0;
    |     ^^^^^
@@ -7,25 +7,25 @@ LL |     x * 0;
    = note: `-D clippy::erasing-op` implied by `-D warnings`
 
 error: this operation will always return zero. This is likely not the intended outcome
-  --> $DIR/erasing_op.rs:38:5
+  --> $DIR/erasing_op.rs:36:5
    |
 LL |     0 & x;
    |     ^^^^^
 
 error: this operation will always return zero. This is likely not the intended outcome
-  --> $DIR/erasing_op.rs:39:5
+  --> $DIR/erasing_op.rs:37:5
    |
 LL |     0 / x;
    |     ^^^^^
 
 error: this operation will always return zero. This is likely not the intended outcome
-  --> $DIR/erasing_op.rs:41:5
+  --> $DIR/erasing_op.rs:39:5
    |
 LL |     0 * Vec1 { x: 5 };
    |     ^^^^^^^^^^^^^^^^^
 
 error: this operation will always return zero. This is likely not the intended outcome
-  --> $DIR/erasing_op.rs:42:5
+  --> $DIR/erasing_op.rs:40:5
    |
 LL |     Vec1 { x: 5 } * 0;
    |     ^^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/format.fixed b/src/tools/clippy/tests/ui/format.fixed
index cd2f70ee8b0..beedf2c1db2 100644
--- a/src/tools/clippy/tests/ui/format.fixed
+++ b/src/tools/clippy/tests/ui/format.fixed
@@ -1,5 +1,4 @@
 // run-rustfix
-// aux-build: proc_macro_with_span.rs
 #![warn(clippy::useless_format)]
 #![allow(
     unused_tuple_struct_fields,
@@ -10,8 +9,6 @@
     clippy::uninlined_format_args
 )]
 
-extern crate proc_macro_with_span;
-
 struct Foo(pub String);
 
 macro_rules! foo {
@@ -90,7 +87,4 @@ fn main() {
     let _ = abc.to_string();
     let xx = "xx";
     let _ = xx.to_string();
-
-    // Issue #10148
-    println!(proc_macro_with_span::with_span!(""something ""));
 }
diff --git a/src/tools/clippy/tests/ui/format.rs b/src/tools/clippy/tests/ui/format.rs
index c22345a79d4..e805f181889 100644
--- a/src/tools/clippy/tests/ui/format.rs
+++ b/src/tools/clippy/tests/ui/format.rs
@@ -1,5 +1,4 @@
 // run-rustfix
-// aux-build: proc_macro_with_span.rs
 #![warn(clippy::useless_format)]
 #![allow(
     unused_tuple_struct_fields,
@@ -10,8 +9,6 @@
     clippy::uninlined_format_args
 )]
 
-extern crate proc_macro_with_span;
-
 struct Foo(pub String);
 
 macro_rules! foo {
@@ -92,7 +89,4 @@ fn main() {
     let _ = format!("{abc}");
     let xx = "xx";
     let _ = format!("{xx}");
-
-    // Issue #10148
-    println!(proc_macro_with_span::with_span!(""something ""));
 }
diff --git a/src/tools/clippy/tests/ui/format.stderr b/src/tools/clippy/tests/ui/format.stderr
index a0e5d5c8ad2..0ef0ac655d3 100644
--- a/src/tools/clippy/tests/ui/format.stderr
+++ b/src/tools/clippy/tests/ui/format.stderr
@@ -1,5 +1,5 @@
 error: useless use of `format!`
-  --> $DIR/format.rs:22:5
+  --> $DIR/format.rs:19:5
    |
 LL |     format!("foo");
    |     ^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string()`
@@ -7,19 +7,19 @@ LL |     format!("foo");
    = note: `-D clippy::useless-format` implied by `-D warnings`
 
 error: useless use of `format!`
-  --> $DIR/format.rs:23:5
+  --> $DIR/format.rs:20:5
    |
 LL |     format!("{{}}");
    |     ^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"{}".to_string()`
 
 error: useless use of `format!`
-  --> $DIR/format.rs:24:5
+  --> $DIR/format.rs:21:5
    |
 LL |     format!("{{}} abc {{}}");
    |     ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"{} abc {}".to_string()`
 
 error: useless use of `format!`
-  --> $DIR/format.rs:25:5
+  --> $DIR/format.rs:22:5
    |
 LL | /     format!(
 LL | |         r##"foo {{}}
@@ -34,67 +34,67 @@ LL ~ " bar"##.to_string();
    |
 
 error: useless use of `format!`
-  --> $DIR/format.rs:30:13
+  --> $DIR/format.rs:27:13
    |
 LL |     let _ = format!("");
    |             ^^^^^^^^^^^ help: consider using `String::new()`: `String::new()`
 
 error: useless use of `format!`
-  --> $DIR/format.rs:32:5
+  --> $DIR/format.rs:29:5
    |
 LL |     format!("{}", "foo");
    |     ^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string()`
 
 error: useless use of `format!`
-  --> $DIR/format.rs:40:5
+  --> $DIR/format.rs:37:5
    |
 LL |     format!("{}", arg);
    |     ^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `arg.to_string()`
 
 error: useless use of `format!`
-  --> $DIR/format.rs:70:5
+  --> $DIR/format.rs:67:5
    |
 LL |     format!("{}", 42.to_string());
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `42.to_string()`
 
 error: useless use of `format!`
-  --> $DIR/format.rs:72:5
+  --> $DIR/format.rs:69:5
    |
 LL |     format!("{}", x.display().to_string());
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.display().to_string()`
 
 error: useless use of `format!`
-  --> $DIR/format.rs:76:18
+  --> $DIR/format.rs:73:18
    |
 LL |     let _ = Some(format!("{}", a + "bar"));
    |                  ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `a + "bar"`
 
 error: useless use of `format!`
-  --> $DIR/format.rs:80:22
+  --> $DIR/format.rs:77:22
    |
 LL |     let _s: String = format!("{}", &*v.join("/n"));
    |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `(&*v.join("/n")).to_string()`
 
 error: useless use of `format!`
-  --> $DIR/format.rs:86:13
+  --> $DIR/format.rs:83:13
    |
 LL |     let _ = format!("{x}");
    |             ^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.to_string()`
 
 error: useless use of `format!`
-  --> $DIR/format.rs:88:13
+  --> $DIR/format.rs:85:13
    |
 LL |     let _ = format!("{y}", y = x);
    |             ^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.to_string()`
 
 error: useless use of `format!`
-  --> $DIR/format.rs:92:13
+  --> $DIR/format.rs:89:13
    |
 LL |     let _ = format!("{abc}");
    |             ^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `abc.to_string()`
 
 error: useless use of `format!`
-  --> $DIR/format.rs:94:13
+  --> $DIR/format.rs:91:13
    |
 LL |     let _ = format!("{xx}");
    |             ^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `xx.to_string()`
diff --git a/src/tools/clippy/tests/ui/impl_trait_in_params.stderr b/src/tools/clippy/tests/ui/impl_trait_in_params.stderr
index acfcc21445e..80383743525 100644
--- a/src/tools/clippy/tests/ui/impl_trait_in_params.stderr
+++ b/src/tools/clippy/tests/ui/impl_trait_in_params.stderr
@@ -5,7 +5,7 @@ LL | pub fn a(_: impl Trait) {}
    |             ^^^^^^^^^^
    |
    = note: `-D clippy::impl-trait-in-params` implied by `-D warnings`
-help: add a type paremeter
+help: add a type parameter
    |
 LL | pub fn a<{ /* Generic name */ }: Trait>(_: impl Trait) {}
    |         +++++++++++++++++++++++++++++++
@@ -16,7 +16,7 @@ error: '`impl Trait` used as a function parameter'
 LL | pub fn c<C: Trait>(_: C, _: impl Trait) {}
    |                             ^^^^^^^^^^
    |
-help: add a type paremeter
+help: add a type parameter
    |
 LL | pub fn c<C: Trait, { /* Generic name */ }: Trait>(_: C, _: impl Trait) {}
    |                  +++++++++++++++++++++++++++++++
diff --git a/src/tools/clippy/tests/ui/implicit_clone.fixed b/src/tools/clippy/tests/ui/implicit_clone.fixed
index 51b1afbe5ac..8ccc3da7b47 100644
--- a/src/tools/clippy/tests/ui/implicit_clone.fixed
+++ b/src/tools/clippy/tests/ui/implicit_clone.fixed
@@ -87,7 +87,7 @@ fn main() {
     let kitten = Kitten {};
     let _ = kitten.clone();
     let _ = own_same_from_ref(&kitten);
-    // this shouln't lint
+    // this shouldn't lint
     let _ = kitten.to_vec();
 
     // we expect no lints for this
diff --git a/src/tools/clippy/tests/ui/implicit_clone.rs b/src/tools/clippy/tests/ui/implicit_clone.rs
index 8a9027433d9..59333312607 100644
--- a/src/tools/clippy/tests/ui/implicit_clone.rs
+++ b/src/tools/clippy/tests/ui/implicit_clone.rs
@@ -87,7 +87,7 @@ fn main() {
     let kitten = Kitten {};
     let _ = kitten.to_owned();
     let _ = own_same_from_ref(&kitten);
-    // this shouln't lint
+    // this shouldn't lint
     let _ = kitten.to_vec();
 
     // we expect no lints for this
diff --git a/src/tools/clippy/tests/ui/integer_arithmetic.rs b/src/tools/clippy/tests/ui/integer_arithmetic.rs
index 67f24b4548a..8dfdee662b9 100644
--- a/src/tools/clippy/tests/ui/integer_arithmetic.rs
+++ b/src/tools/clippy/tests/ui/integer_arithmetic.rs
@@ -4,7 +4,7 @@
 #[rustfmt::skip]
 fn main() {
     let mut i = 1i32;
-    let mut var1 = 0i32;
+    let mut var1 = 13i32;
     let mut var2 = -1i32;
     1 + i;
     i * 2;
diff --git a/src/tools/clippy/tests/ui/len_without_is_empty.rs b/src/tools/clippy/tests/ui/len_without_is_empty.rs
index b5dec6c46bd..52aabefaed2 100644
--- a/src/tools/clippy/tests/ui/len_without_is_empty.rs
+++ b/src/tools/clippy/tests/ui/len_without_is_empty.rs
@@ -282,6 +282,87 @@ impl AsyncLen {
     }
 }
 
+// issue #7232
+pub struct AsyncLenWithoutIsEmpty;
+impl AsyncLenWithoutIsEmpty {
+    pub async fn async_task(&self) -> bool {
+        true
+    }
+
+    pub async fn len(&self) -> usize {
+        usize::from(!self.async_task().await)
+    }
+}
+
+// issue #7232
+pub struct AsyncOptionLenWithoutIsEmpty;
+impl AsyncOptionLenWithoutIsEmpty {
+    async fn async_task(&self) -> bool {
+        true
+    }
+
+    pub async fn len(&self) -> Option<usize> {
+        None
+    }
+}
+
+// issue #7232
+pub struct AsyncOptionLenNonIntegral;
+impl AsyncOptionLenNonIntegral {
+    // don't lint
+    pub async fn len(&self) -> Option<String> {
+        None
+    }
+}
+
+// issue #7232
+pub struct AsyncResultLenWithoutIsEmpty;
+impl AsyncResultLenWithoutIsEmpty {
+    async fn async_task(&self) -> bool {
+        true
+    }
+
+    pub async fn len(&self) -> Result<usize, ()> {
+        Err(())
+    }
+}
+
+// issue #7232
+pub struct AsyncOptionLen;
+impl AsyncOptionLen {
+    async fn async_task(&self) -> bool {
+        true
+    }
+
+    pub async fn len(&self) -> Result<usize, ()> {
+        Err(())
+    }
+
+    pub async fn is_empty(&self) -> bool {
+        true
+    }
+}
+
+pub struct AsyncLenSyncIsEmpty;
+impl AsyncLenSyncIsEmpty {
+    pub async fn len(&self) -> u32 {
+        0
+    }
+
+    pub fn is_empty(&self) -> bool {
+        true
+    }
+}
+
+// issue #9520
+pub struct NonStandardLen;
+impl NonStandardLen {
+    // don't lint
+    pub fn len(&self, something: usize) -> usize {
+        something
+    }
+}
+
 // issue #9520
 pub struct NonStandardLenAndIsEmptySignature;
 impl NonStandardLenAndIsEmptySignature {
@@ -328,4 +409,15 @@ impl NonStandardSignatureWithGenerics {
     }
 }
 
+pub struct DifferingErrors;
+impl DifferingErrors {
+    pub fn len(&self) -> Result<usize, u8> {
+        Ok(0)
+    }
+
+    pub fn is_empty(&self) -> Result<bool, u16> {
+        Ok(true)
+    }
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/len_without_is_empty.stderr b/src/tools/clippy/tests/ui/len_without_is_empty.stderr
index 8e890e2e259..1bce1734b81 100644
--- a/src/tools/clippy/tests/ui/len_without_is_empty.stderr
+++ b/src/tools/clippy/tests/ui/len_without_is_empty.stderr
@@ -119,5 +119,23 @@ LL |     pub fn len(&self) -> Result<usize, ()> {
    |
    = help: use a custom `Error` type instead
 
-error: aborting due to 12 previous errors
+error: struct `AsyncLenWithoutIsEmpty` has a public `len` method, but no `is_empty` method
+  --> $DIR/len_without_is_empty.rs:292:5
+   |
+LL |     pub async fn len(&self) -> usize {
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: struct `AsyncOptionLenWithoutIsEmpty` has a public `len` method, but no `is_empty` method
+  --> $DIR/len_without_is_empty.rs:304:5
+   |
+LL |     pub async fn len(&self) -> Option<usize> {
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: struct `AsyncResultLenWithoutIsEmpty` has a public `len` method, but no `is_empty` method
+  --> $DIR/len_without_is_empty.rs:325:5
+   |
+LL |     pub async fn len(&self) -> Result<usize, ()> {
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 15 previous errors
 
diff --git a/src/tools/clippy/tests/ui/let_unit.fixed b/src/tools/clippy/tests/ui/let_unit.fixed
index 6343cff0f7f..76ff0645f41 100644
--- a/src/tools/clippy/tests/ui/let_unit.fixed
+++ b/src/tools/clippy/tests/ui/let_unit.fixed
@@ -175,3 +175,7 @@ fn attributes() {
     #[expect(clippy::let_unit_value)]
     let _ = f();
 }
+
+async fn issue10433() {
+    let _pending: () = std::future::pending().await;
+}
diff --git a/src/tools/clippy/tests/ui/let_unit.rs b/src/tools/clippy/tests/ui/let_unit.rs
index c9bb2849f5c..895ccfe366a 100644
--- a/src/tools/clippy/tests/ui/let_unit.rs
+++ b/src/tools/clippy/tests/ui/let_unit.rs
@@ -175,3 +175,7 @@ fn attributes() {
     #[expect(clippy::let_unit_value)]
     let _ = f();
 }
+
+async fn issue10433() {
+    let _pending: () = std::future::pending().await;
+}
diff --git a/src/tools/clippy/tests/ui/let_with_type_underscore.rs b/src/tools/clippy/tests/ui/let_with_type_underscore.rs
new file mode 100644
index 00000000000..175718b94c8
--- /dev/null
+++ b/src/tools/clippy/tests/ui/let_with_type_underscore.rs
@@ -0,0 +1,19 @@
+#![allow(unused)]
+#![warn(clippy::let_with_type_underscore)]
+#![allow(clippy::let_unit_value)]
+
+fn func() -> &'static str {
+    ""
+}
+
+fn main() {
+    // Will lint
+    let x: _ = 1;
+    let _: _ = 2;
+    let x: _ = func();
+
+    let x = 1; // Will not lint, Rust inferres this to an integer before Clippy
+    let x = func();
+    let x: Vec<_> = Vec::<u32>::new();
+    let x: [_; 1] = [1];
+}
diff --git a/src/tools/clippy/tests/ui/let_with_type_underscore.stderr b/src/tools/clippy/tests/ui/let_with_type_underscore.stderr
new file mode 100644
index 00000000000..16bf83c708f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/let_with_type_underscore.stderr
@@ -0,0 +1,39 @@
+error: variable declared with type underscore
+  --> $DIR/let_with_type_underscore.rs:11:5
+   |
+LL |     let x: _ = 1;
+   |     ^^^^^^^^^^^^^
+   |
+help: remove the explicit type `_` declaration
+  --> $DIR/let_with_type_underscore.rs:11:10
+   |
+LL |     let x: _ = 1;
+   |          ^^^
+   = note: `-D clippy::let-with-type-underscore` implied by `-D warnings`
+
+error: variable declared with type underscore
+  --> $DIR/let_with_type_underscore.rs:12:5
+   |
+LL |     let _: _ = 2;
+   |     ^^^^^^^^^^^^^
+   |
+help: remove the explicit type `_` declaration
+  --> $DIR/let_with_type_underscore.rs:12:10
+   |
+LL |     let _: _ = 2;
+   |          ^^^
+
+error: variable declared with type underscore
+  --> $DIR/let_with_type_underscore.rs:13:5
+   |
+LL |     let x: _ = func();
+   |     ^^^^^^^^^^^^^^^^^^
+   |
+help: remove the explicit type `_` declaration
+  --> $DIR/let_with_type_underscore.rs:13:10
+   |
+LL |     let x: _ = func();
+   |          ^^^
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/manual_rem_euclid.fixed b/src/tools/clippy/tests/ui/manual_rem_euclid.fixed
index 4cdc0546a74..6916a284a20 100644
--- a/src/tools/clippy/tests/ui/manual_rem_euclid.fixed
+++ b/src/tools/clippy/tests/ui/manual_rem_euclid.fixed
@@ -2,6 +2,7 @@
 // aux-build:macro_rules.rs
 
 #![warn(clippy::manual_rem_euclid)]
+#![allow(clippy::let_with_type_underscore)]
 
 #[macro_use]
 extern crate macro_rules;
diff --git a/src/tools/clippy/tests/ui/manual_rem_euclid.rs b/src/tools/clippy/tests/ui/manual_rem_euclid.rs
index 58a9e20f38b..412dbddb426 100644
--- a/src/tools/clippy/tests/ui/manual_rem_euclid.rs
+++ b/src/tools/clippy/tests/ui/manual_rem_euclid.rs
@@ -2,6 +2,7 @@
 // aux-build:macro_rules.rs
 
 #![warn(clippy::manual_rem_euclid)]
+#![allow(clippy::let_with_type_underscore)]
 
 #[macro_use]
 extern crate macro_rules;
diff --git a/src/tools/clippy/tests/ui/manual_rem_euclid.stderr b/src/tools/clippy/tests/ui/manual_rem_euclid.stderr
index e3122a588b6..6d06654638b 100644
--- a/src/tools/clippy/tests/ui/manual_rem_euclid.stderr
+++ b/src/tools/clippy/tests/ui/manual_rem_euclid.stderr
@@ -1,5 +1,5 @@
 error: manual `rem_euclid` implementation
-  --> $DIR/manual_rem_euclid.rs:19:18
+  --> $DIR/manual_rem_euclid.rs:20:18
    |
 LL |     let _: i32 = ((value % 4) + 4) % 4;
    |                  ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)`
@@ -7,31 +7,31 @@ LL |     let _: i32 = ((value % 4) + 4) % 4;
    = note: `-D clippy::manual-rem-euclid` implied by `-D warnings`
 
 error: manual `rem_euclid` implementation
-  --> $DIR/manual_rem_euclid.rs:20:18
+  --> $DIR/manual_rem_euclid.rs:21:18
    |
 LL |     let _: i32 = (4 + (value % 4)) % 4;
    |                  ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)`
 
 error: manual `rem_euclid` implementation
-  --> $DIR/manual_rem_euclid.rs:21:18
+  --> $DIR/manual_rem_euclid.rs:22:18
    |
 LL |     let _: i32 = (value % 4 + 4) % 4;
    |                  ^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)`
 
 error: manual `rem_euclid` implementation
-  --> $DIR/manual_rem_euclid.rs:22:18
+  --> $DIR/manual_rem_euclid.rs:23:18
    |
 LL |     let _: i32 = (4 + value % 4) % 4;
    |                  ^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)`
 
 error: manual `rem_euclid` implementation
-  --> $DIR/manual_rem_euclid.rs:23:22
+  --> $DIR/manual_rem_euclid.rs:24:22
    |
 LL |     let _: i32 = 1 + (4 + value % 4) % 4;
    |                      ^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)`
 
 error: manual `rem_euclid` implementation
-  --> $DIR/manual_rem_euclid.rs:12:22
+  --> $DIR/manual_rem_euclid.rs:13:22
    |
 LL |         let _: i32 = ((value % 4) + 4) % 4;
    |                      ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)`
@@ -42,25 +42,25 @@ LL |     internal_rem_euclid!();
    = note: this error originates in the macro `internal_rem_euclid` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: manual `rem_euclid` implementation
-  --> $DIR/manual_rem_euclid.rs:49:5
+  --> $DIR/manual_rem_euclid.rs:50:5
    |
 LL |     ((num % 4) + 4) % 4
    |     ^^^^^^^^^^^^^^^^^^^ help: consider using: `num.rem_euclid(4)`
 
 error: manual `rem_euclid` implementation
-  --> $DIR/manual_rem_euclid.rs:54:5
+  --> $DIR/manual_rem_euclid.rs:55:5
    |
 LL |     ((num % 4) + 4) % 4
    |     ^^^^^^^^^^^^^^^^^^^ help: consider using: `num.rem_euclid(4)`
 
 error: manual `rem_euclid` implementation
-  --> $DIR/manual_rem_euclid.rs:66:18
+  --> $DIR/manual_rem_euclid.rs:67:18
    |
 LL |     let _: i32 = ((x % 4) + 4) % 4;
    |                  ^^^^^^^^^^^^^^^^^ help: consider using: `x.rem_euclid(4)`
 
 error: manual `rem_euclid` implementation
-  --> $DIR/manual_rem_euclid.rs:79:18
+  --> $DIR/manual_rem_euclid.rs:80:18
    |
 LL |     let _: i32 = ((x % 4) + 4) % 4;
    |                  ^^^^^^^^^^^^^^^^^ help: consider using: `x.rem_euclid(4)`
diff --git a/src/tools/clippy/tests/ui/match_result_ok.fixed b/src/tools/clippy/tests/ui/match_result_ok.fixed
index 8b91b9854a0..10ae1ee5245 100644
--- a/src/tools/clippy/tests/ui/match_result_ok.fixed
+++ b/src/tools/clippy/tests/ui/match_result_ok.fixed
@@ -16,7 +16,7 @@ fn str_to_int_ok(x: &str) -> i32 {
 #[rustfmt::skip]
 fn strange_some_no_else(x: &str) -> i32 {
     {
-        if let Ok(y) = x   .   parse()       {
+        if let Ok(y) = x   .   parse()    {
             return y;
         };
         0
diff --git a/src/tools/clippy/tests/ui/match_result_ok.stderr b/src/tools/clippy/tests/ui/match_result_ok.stderr
index 98a95705ca5..cbdc56aa28c 100644
--- a/src/tools/clippy/tests/ui/match_result_ok.stderr
+++ b/src/tools/clippy/tests/ui/match_result_ok.stderr
@@ -18,7 +18,7 @@ LL |         if let Some(y) = x   .   parse()   .   ok   ()    {
    |
 help: consider matching on `Ok(y)` and removing the call to `ok` instead
    |
-LL |         if let Ok(y) = x   .   parse()       {
+LL |         if let Ok(y) = x   .   parse()    {
    |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: matching on `Some` with `ok()` is redundant
diff --git a/src/tools/clippy/tests/ui/missing_assert_message.rs b/src/tools/clippy/tests/ui/missing_assert_message.rs
new file mode 100644
index 00000000000..89404ca8827
--- /dev/null
+++ b/src/tools/clippy/tests/ui/missing_assert_message.rs
@@ -0,0 +1,84 @@
+#![allow(unused)]
+#![warn(clippy::missing_assert_message)]
+
+macro_rules! bar {
+    ($( $x:expr ),*) => {
+        foo()
+    };
+}
+
+fn main() {}
+
+// Should trigger warning
+fn asserts_without_message() {
+    assert!(foo());
+    assert_eq!(foo(), foo());
+    assert_ne!(foo(), foo());
+    debug_assert!(foo());
+    debug_assert_eq!(foo(), foo());
+    debug_assert_ne!(foo(), foo());
+}
+
+// Should trigger warning
+fn asserts_without_message_but_with_macro_calls() {
+    assert!(bar!(true));
+    assert!(bar!(true, false));
+    assert_eq!(bar!(true), foo());
+    assert_ne!(bar!(true, true), bar!(true));
+}
+
+// Should trigger warning
+fn asserts_with_trailing_commas() {
+    assert!(foo(),);
+    assert_eq!(foo(), foo(),);
+    assert_ne!(foo(), foo(),);
+    debug_assert!(foo(),);
+    debug_assert_eq!(foo(), foo(),);
+    debug_assert_ne!(foo(), foo(),);
+}
+
+// Should not trigger warning
+fn asserts_with_message_and_with_macro_calls() {
+    assert!(bar!(true), "msg");
+    assert!(bar!(true, false), "msg");
+    assert_eq!(bar!(true), foo(), "msg");
+    assert_ne!(bar!(true, true), bar!(true), "msg");
+}
+
+// Should not trigger warning
+fn asserts_with_message() {
+    assert!(foo(), "msg");
+    assert_eq!(foo(), foo(), "msg");
+    assert_ne!(foo(), foo(), "msg");
+    debug_assert!(foo(), "msg");
+    debug_assert_eq!(foo(), foo(), "msg");
+    debug_assert_ne!(foo(), foo(), "msg");
+}
+
+// Should not trigger warning
+#[test]
+fn asserts_without_message_but_inside_a_test_function() {
+    assert!(foo());
+    assert_eq!(foo(), foo());
+    assert_ne!(foo(), foo());
+    debug_assert!(foo());
+    debug_assert_eq!(foo(), foo());
+    debug_assert_ne!(foo(), foo());
+}
+
+// Should not trigger warning
+#[cfg(test)]
+mod tests {
+    fn asserts_without_message_but_inside_a_test_module() {
+        assert!(foo());
+        assert_eq!(foo(), foo());
+        assert_ne!(foo(), foo());
+        debug_assert!(foo());
+        debug_assert_eq!(foo(), foo());
+        debug_assert_ne!(foo(), foo());
+    }
+}
+
+fn foo() -> bool {
+    true
+}
diff --git a/src/tools/clippy/tests/ui/missing_assert_message.stderr b/src/tools/clippy/tests/ui/missing_assert_message.stderr
new file mode 100644
index 00000000000..ecd03801277
--- /dev/null
+++ b/src/tools/clippy/tests/ui/missing_assert_message.stderr
@@ -0,0 +1,131 @@
+error: assert without any message
+  --> $DIR/missing_assert_message.rs:14:5
+   |
+LL |     assert!(foo());
+   |     ^^^^^^^^^^^^^^
+   |
+   = help: consider describing why the failing assert is problematic
+   = note: `-D clippy::missing-assert-message` implied by `-D warnings`
+
+error: assert without any message
+  --> $DIR/missing_assert_message.rs:15:5
+   |
+LL |     assert_eq!(foo(), foo());
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider describing why the failing assert is problematic
+
+error: assert without any message
+  --> $DIR/missing_assert_message.rs:16:5
+   |
+LL |     assert_ne!(foo(), foo());
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider describing why the failing assert is problematic
+
+error: assert without any message
+  --> $DIR/missing_assert_message.rs:17:5
+   |
+LL |     debug_assert!(foo());
+   |     ^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider describing why the failing assert is problematic
+
+error: assert without any message
+  --> $DIR/missing_assert_message.rs:18:5
+   |
+LL |     debug_assert_eq!(foo(), foo());
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider describing why the failing assert is problematic
+
+error: assert without any message
+  --> $DIR/missing_assert_message.rs:19:5
+   |
+LL |     debug_assert_ne!(foo(), foo());
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider describing why the failing assert is problematic
+
+error: assert without any message
+  --> $DIR/missing_assert_message.rs:24:5
+   |
+LL |     assert!(bar!(true));
+   |     ^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider describing why the failing assert is problematic
+
+error: assert without any message
+  --> $DIR/missing_assert_message.rs:25:5
+   |
+LL |     assert!(bar!(true, false));
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider describing why the failing assert is problematic
+
+error: assert without any message
+  --> $DIR/missing_assert_message.rs:26:5
+   |
+LL |     assert_eq!(bar!(true), foo());
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider describing why the failing assert is problematic
+
+error: assert without any message
+  --> $DIR/missing_assert_message.rs:27:5
+   |
+LL |     assert_ne!(bar!(true, true), bar!(true));
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider describing why the failing assert is problematic
+
+error: assert without any message
+  --> $DIR/missing_assert_message.rs:32:5
+   |
+LL |     assert!(foo(),);
+   |     ^^^^^^^^^^^^^^^
+   |
+   = help: consider describing why the failing assert is problematic
+
+error: assert without any message
+  --> $DIR/missing_assert_message.rs:33:5
+   |
+LL |     assert_eq!(foo(), foo(),);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider describing why the failing assert is problematic
+
+error: assert without any message
+  --> $DIR/missing_assert_message.rs:34:5
+   |
+LL |     assert_ne!(foo(), foo(),);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider describing why the failing assert is problematic
+
+error: assert without any message
+  --> $DIR/missing_assert_message.rs:35:5
+   |
+LL |     debug_assert!(foo(),);
+   |     ^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider describing why the failing assert is problematic
+
+error: assert without any message
+  --> $DIR/missing_assert_message.rs:36:5
+   |
+LL |     debug_assert_eq!(foo(), foo(),);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider describing why the failing assert is problematic
+
+error: assert without any message
+  --> $DIR/missing_assert_message.rs:37:5
+   |
+LL |     debug_assert_ne!(foo(), foo(),);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider describing why the failing assert is problematic
+
+error: aborting due to 16 previous errors
+
diff --git a/src/tools/clippy/tests/ui/missing_doc.stderr b/src/tools/clippy/tests/ui/missing_doc.stderr
index d3bef28bf64..4e8a49bf1cd 100644
--- a/src/tools/clippy/tests/ui/missing_doc.stderr
+++ b/src/tools/clippy/tests/ui/missing_doc.stderr
@@ -6,30 +6,12 @@ LL | type Typedef = String;
    |
    = note: `-D clippy::missing-docs-in-private-items` implied by `-D warnings`
 
-error: missing documentation for a type alias
-  --> $DIR/missing_doc.rs:17:1
-   |
-LL | pub type PubTypedef = String;
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
 error: missing documentation for a module
   --> $DIR/missing_doc.rs:19:1
    |
 LL | mod module_no_dox {}
    | ^^^^^^^^^^^^^^^^^^^^
 
-error: missing documentation for a module
-  --> $DIR/missing_doc.rs:20:1
-   |
-LL | pub mod pub_module_no_dox {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: missing documentation for a function
-  --> $DIR/missing_doc.rs:24:1
-   |
-LL | pub fn foo2() {}
-   | ^^^^^^^^^^^^^^^^
-
 error: missing documentation for a function
   --> $DIR/missing_doc.rs:25:1
    |
@@ -69,50 +51,18 @@ error: missing documentation for a variant
 LL |     BarB,
    |     ^^^^
 
-error: missing documentation for an enum
-  --> $DIR/missing_doc.rs:44:1
-   |
-LL | / pub enum PubBaz {
-LL | |     PubBazA { a: isize },
-LL | | }
-   | |_^
-
-error: missing documentation for a variant
-  --> $DIR/missing_doc.rs:45:5
-   |
-LL |     PubBazA { a: isize },
-   |     ^^^^^^^^^^^^^^^^^^^^
-
-error: missing documentation for a struct field
-  --> $DIR/missing_doc.rs:45:15
-   |
-LL |     PubBazA { a: isize },
-   |               ^^^^^^^^
-
 error: missing documentation for a constant
   --> $DIR/missing_doc.rs:65:1
    |
 LL | const FOO: u32 = 0;
    | ^^^^^^^^^^^^^^^^^^^
 
-error: missing documentation for a constant
-  --> $DIR/missing_doc.rs:72:1
-   |
-LL | pub const FOO4: u32 = 0;
-   | ^^^^^^^^^^^^^^^^^^^^^^^^
-
 error: missing documentation for a static
   --> $DIR/missing_doc.rs:74:1
    |
 LL | static BAR: u32 = 0;
    | ^^^^^^^^^^^^^^^^^^^^
 
-error: missing documentation for a static
-  --> $DIR/missing_doc.rs:81:1
-   |
-LL | pub static BAR4: u32 = 0;
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^
-
 error: missing documentation for a module
   --> $DIR/missing_doc.rs:83:1
    |
@@ -126,34 +76,16 @@ LL | | }
    | |_^
 
 error: missing documentation for a function
-  --> $DIR/missing_doc.rs:86:5
-   |
-LL |     pub fn undocumented1() {}
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: missing documentation for a function
-  --> $DIR/missing_doc.rs:87:5
-   |
-LL |     pub fn undocumented2() {}
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: missing documentation for a function
   --> $DIR/missing_doc.rs:88:5
    |
 LL |     fn undocumented3() {}
    |     ^^^^^^^^^^^^^^^^^^^^^
 
 error: missing documentation for a function
-  --> $DIR/missing_doc.rs:93:9
-   |
-LL |         pub fn also_undocumented1() {}
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: missing documentation for a function
   --> $DIR/missing_doc.rs:94:9
    |
 LL |         fn also_undocumented2() {}
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 24 previous errors
+error: aborting due to 13 previous errors
 
diff --git a/src/tools/clippy/tests/ui/missing_doc_impl.stderr b/src/tools/clippy/tests/ui/missing_doc_impl.stderr
index b410f56e167..111d6546966 100644
--- a/src/tools/clippy/tests/ui/missing_doc_impl.stderr
+++ b/src/tools/clippy/tests/ui/missing_doc_impl.stderr
@@ -21,60 +21,12 @@ error: missing documentation for a struct field
 LL |     b: isize,
    |     ^^^^^^^^
 
-error: missing documentation for a struct
-  --> $DIR/missing_doc_impl.rs:18:1
-   |
-LL | / pub struct PubFoo {
-LL | |     pub a: isize,
-LL | |     b: isize,
-LL | | }
-   | |_^
-
-error: missing documentation for a struct field
-  --> $DIR/missing_doc_impl.rs:19:5
-   |
-LL |     pub a: isize,
-   |     ^^^^^^^^^^^^
-
 error: missing documentation for a struct field
   --> $DIR/missing_doc_impl.rs:20:5
    |
 LL |     b: isize,
    |     ^^^^^^^^
 
-error: missing documentation for a trait
-  --> $DIR/missing_doc_impl.rs:43:1
-   |
-LL | / pub trait C {
-LL | |     fn foo(&self);
-LL | |     fn foo_with_impl(&self) {}
-LL | | }
-   | |_^
-
-error: missing documentation for a method
-  --> $DIR/missing_doc_impl.rs:44:5
-   |
-LL |     fn foo(&self);
-   |     ^^^^^^^^^^^^^^
-
-error: missing documentation for a method
-  --> $DIR/missing_doc_impl.rs:45:5
-   |
-LL |     fn foo_with_impl(&self) {}
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: missing documentation for an associated type
-  --> $DIR/missing_doc_impl.rs:55:5
-   |
-LL |     type AssociatedType;
-   |     ^^^^^^^^^^^^^^^^^^^^
-
-error: missing documentation for an associated type
-  --> $DIR/missing_doc_impl.rs:56:5
-   |
-LL |     type AssociatedTypeDef = Self;
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
 error: missing documentation for an associated function
   --> $DIR/missing_doc_impl.rs:67:5
    |
@@ -90,12 +42,6 @@ LL |     fn bar() {}
    |     ^^^^^^^^^^^
 
 error: missing documentation for an associated function
-  --> $DIR/missing_doc_impl.rs:74:5
-   |
-LL |     pub fn foo() {}
-   |     ^^^^^^^^^^^^^^^
-
-error: missing documentation for an associated function
   --> $DIR/missing_doc_impl.rs:78:5
    |
 LL | /     fn foo2() -> u32 {
@@ -103,5 +49,5 @@ LL | |         1
 LL | |     }
    | |_____^
 
-error: aborting due to 15 previous errors
+error: aborting due to 7 previous errors
 
diff --git a/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.rs b/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.rs
index 4511bc99c3c..5073685c9f0 100644
--- a/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.rs
+++ b/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.rs
@@ -116,4 +116,32 @@ fn issue10259() {
     unsafe_macro!();
 }
 
+fn _fn_ptr(x: unsafe fn()) {
+    unsafe {
+        x();
+        x();
+    }
+}
+
+fn _assoc_const() {
+    trait X {
+        const X: unsafe fn();
+    }
+    fn _f<T: X>() {
+        unsafe {
+            T::X();
+            T::X();
+        }
+    }
+}
+
+fn _field_fn_ptr(x: unsafe fn()) {
+    struct X(unsafe fn());
+    let x = X(x);
+    unsafe {
+        x.0();
+        x.0();
+    }
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.stderr b/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.stderr
index 303aeb7aee0..e0c1d3801f7 100644
--- a/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.stderr
+++ b/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.stderr
@@ -125,5 +125,65 @@ note: raw pointer dereference occurs here
 LL |     unsafe { char::from_u32_unchecked(*ptr.cast::<u32>()) }
    |                                       ^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 5 previous errors
+error: this `unsafe` block contains 2 unsafe operations, expected only one
+  --> $DIR/multiple_unsafe_ops_per_block.rs:120:5
+   |
+LL | /     unsafe {
+LL | |         x();
+LL | |         x();
+LL | |     }
+   | |_____^
+   |
+note: unsafe function call occurs here
+  --> $DIR/multiple_unsafe_ops_per_block.rs:121:9
+   |
+LL |         x();
+   |         ^^^
+note: unsafe function call occurs here
+  --> $DIR/multiple_unsafe_ops_per_block.rs:122:9
+   |
+LL |         x();
+   |         ^^^
+
+error: this `unsafe` block contains 2 unsafe operations, expected only one
+  --> $DIR/multiple_unsafe_ops_per_block.rs:131:9
+   |
+LL | /         unsafe {
+LL | |             T::X();
+LL | |             T::X();
+LL | |         }
+   | |_________^
+   |
+note: unsafe function call occurs here
+  --> $DIR/multiple_unsafe_ops_per_block.rs:132:13
+   |
+LL |             T::X();
+   |             ^^^^^^
+note: unsafe function call occurs here
+  --> $DIR/multiple_unsafe_ops_per_block.rs:133:13
+   |
+LL |             T::X();
+   |             ^^^^^^
+
+error: this `unsafe` block contains 2 unsafe operations, expected only one
+  --> $DIR/multiple_unsafe_ops_per_block.rs:141:5
+   |
+LL | /     unsafe {
+LL | |         x.0();
+LL | |         x.0();
+LL | |     }
+   | |_____^
+   |
+note: unsafe function call occurs here
+  --> $DIR/multiple_unsafe_ops_per_block.rs:142:9
+   |
+LL |         x.0();
+   |         ^^^^^
+note: unsafe function call occurs here
+  --> $DIR/multiple_unsafe_ops_per_block.rs:143:9
+   |
+LL |         x.0();
+   |         ^^^^^
+
+error: aborting due to 8 previous errors
 
diff --git a/src/tools/clippy/tests/ui/new_ret_no_self.rs b/src/tools/clippy/tests/ui/new_ret_no_self.rs
index beec42f08bb..a2a30c8b931 100644
--- a/src/tools/clippy/tests/ui/new_ret_no_self.rs
+++ b/src/tools/clippy/tests/ui/new_ret_no_self.rs
@@ -406,7 +406,7 @@ mod issue10041 {
     struct Bomb;
 
     impl Bomb {
-        // Hidden <Rhs = Self> default generic paramter.
+        // Hidden <Rhs = Self> default generic parameter.
         pub fn new() -> impl PartialOrd {
             0i32
         }
diff --git a/src/tools/clippy/tests/ui/no_effect.rs b/src/tools/clippy/tests/ui/no_effect.rs
index f08eb092e6b..ec8a5aa28c5 100644
--- a/src/tools/clippy/tests/ui/no_effect.rs
+++ b/src/tools/clippy/tests/ui/no_effect.rs
@@ -1,4 +1,4 @@
-#![feature(box_syntax, fn_traits, unboxed_closures)]
+#![feature(fn_traits, unboxed_closures)]
 #![warn(clippy::no_effect_underscore_binding)]
 #![allow(dead_code, path_statements)]
 #![allow(clippy::deref_addrof, clippy::redundant_field_names, clippy::uninlined_format_args)]
@@ -102,7 +102,6 @@ fn main() {
     *&42;
     &6;
     (5, 6, 7);
-    box 42;
     ..;
     5..;
     ..5;
diff --git a/src/tools/clippy/tests/ui/no_effect.stderr b/src/tools/clippy/tests/ui/no_effect.stderr
index 6a1e636f9a6..92f6dbfbdba 100644
--- a/src/tools/clippy/tests/ui/no_effect.stderr
+++ b/src/tools/clippy/tests/ui/no_effect.stderr
@@ -81,83 +81,77 @@ LL |     (5, 6, 7);
 error: statement with no effect
   --> $DIR/no_effect.rs:105:5
    |
-LL |     box 42;
-   |     ^^^^^^^
-
-error: statement with no effect
-  --> $DIR/no_effect.rs:106:5
-   |
 LL |     ..;
    |     ^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:107:5
+  --> $DIR/no_effect.rs:106:5
    |
 LL |     5..;
    |     ^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:108:5
+  --> $DIR/no_effect.rs:107:5
    |
 LL |     ..5;
    |     ^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:109:5
+  --> $DIR/no_effect.rs:108:5
    |
 LL |     5..6;
    |     ^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:110:5
+  --> $DIR/no_effect.rs:109:5
    |
 LL |     5..=6;
    |     ^^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:111:5
+  --> $DIR/no_effect.rs:110:5
    |
 LL |     [42, 55];
    |     ^^^^^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:112:5
+  --> $DIR/no_effect.rs:111:5
    |
 LL |     [42, 55][1];
    |     ^^^^^^^^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:113:5
+  --> $DIR/no_effect.rs:112:5
    |
 LL |     (42, 55).1;
    |     ^^^^^^^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:114:5
+  --> $DIR/no_effect.rs:113:5
    |
 LL |     [42; 55];
    |     ^^^^^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:115:5
+  --> $DIR/no_effect.rs:114:5
    |
 LL |     [42; 55][13];
    |     ^^^^^^^^^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:117:5
+  --> $DIR/no_effect.rs:116:5
    |
 LL |     || x += 5;
    |     ^^^^^^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:119:5
+  --> $DIR/no_effect.rs:118:5
    |
 LL |     FooString { s: s };
    |     ^^^^^^^^^^^^^^^^^^^
 
 error: binding to `_` prefixed variable with no side-effect
-  --> $DIR/no_effect.rs:120:5
+  --> $DIR/no_effect.rs:119:5
    |
 LL |     let _unused = 1;
    |     ^^^^^^^^^^^^^^^^
@@ -165,22 +159,22 @@ LL |     let _unused = 1;
    = note: `-D clippy::no-effect-underscore-binding` implied by `-D warnings`
 
 error: binding to `_` prefixed variable with no side-effect
-  --> $DIR/no_effect.rs:121:5
+  --> $DIR/no_effect.rs:120:5
    |
 LL |     let _penguin = || println!("Some helpful closure");
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: binding to `_` prefixed variable with no side-effect
-  --> $DIR/no_effect.rs:122:5
+  --> $DIR/no_effect.rs:121:5
    |
 LL |     let _duck = Struct { field: 0 };
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: binding to `_` prefixed variable with no side-effect
-  --> $DIR/no_effect.rs:123:5
+  --> $DIR/no_effect.rs:122:5
    |
 LL |     let _cat = [2, 4, 6, 8][2];
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 30 previous errors
+error: aborting due to 29 previous errors
 
diff --git a/src/tools/clippy/tests/ui/overflow_check_conditional.rs b/src/tools/clippy/tests/ui/overflow_check_conditional.rs
index 5db75f5291b..e1e30114081 100644
--- a/src/tools/clippy/tests/ui/overflow_check_conditional.rs
+++ b/src/tools/clippy/tests/ui/overflow_check_conditional.rs
@@ -1,9 +1,6 @@
 #![warn(clippy::overflow_check_conditional)]
 
-fn main() {
-    let a: u32 = 1;
-    let b: u32 = 2;
-    let c: u32 = 3;
+fn test(a: u32, b: u32, c: u32) {
     if a + b < a {}
     if a > a + b {}
     if a + b < b {}
@@ -23,3 +20,7 @@ fn main() {
     if i > i + j {}
     if i - j < i {}
 }
+
+fn main() {
+    test(1, 2, 3)
+}
diff --git a/src/tools/clippy/tests/ui/overflow_check_conditional.stderr b/src/tools/clippy/tests/ui/overflow_check_conditional.stderr
index 1b8b146b60a..92d1d8ef911 100644
--- a/src/tools/clippy/tests/ui/overflow_check_conditional.stderr
+++ b/src/tools/clippy/tests/ui/overflow_check_conditional.stderr
@@ -1,5 +1,5 @@
 error: you are trying to use classic C overflow conditions that will fail in Rust
-  --> $DIR/overflow_check_conditional.rs:7:8
+  --> $DIR/overflow_check_conditional.rs:4:8
    |
 LL |     if a + b < a {}
    |        ^^^^^^^^^
@@ -7,43 +7,43 @@ LL |     if a + b < a {}
    = note: `-D clippy::overflow-check-conditional` implied by `-D warnings`
 
 error: you are trying to use classic C overflow conditions that will fail in Rust
-  --> $DIR/overflow_check_conditional.rs:8:8
+  --> $DIR/overflow_check_conditional.rs:5:8
    |
 LL |     if a > a + b {}
    |        ^^^^^^^^^
 
 error: you are trying to use classic C overflow conditions that will fail in Rust
-  --> $DIR/overflow_check_conditional.rs:9:8
+  --> $DIR/overflow_check_conditional.rs:6:8
    |
 LL |     if a + b < b {}
    |        ^^^^^^^^^
 
 error: you are trying to use classic C overflow conditions that will fail in Rust
-  --> $DIR/overflow_check_conditional.rs:10:8
+  --> $DIR/overflow_check_conditional.rs:7:8
    |
 LL |     if b > a + b {}
    |        ^^^^^^^^^
 
 error: you are trying to use classic C underflow conditions that will fail in Rust
-  --> $DIR/overflow_check_conditional.rs:11:8
+  --> $DIR/overflow_check_conditional.rs:8:8
    |
 LL |     if a - b > b {}
    |        ^^^^^^^^^
 
 error: you are trying to use classic C underflow conditions that will fail in Rust
-  --> $DIR/overflow_check_conditional.rs:12:8
+  --> $DIR/overflow_check_conditional.rs:9:8
    |
 LL |     if b < a - b {}
    |        ^^^^^^^^^
 
 error: you are trying to use classic C underflow conditions that will fail in Rust
-  --> $DIR/overflow_check_conditional.rs:13:8
+  --> $DIR/overflow_check_conditional.rs:10:8
    |
 LL |     if a - b > a {}
    |        ^^^^^^^^^
 
 error: you are trying to use classic C underflow conditions that will fail in Rust
-  --> $DIR/overflow_check_conditional.rs:14:8
+  --> $DIR/overflow_check_conditional.rs:11:8
    |
 LL |     if a < a - b {}
    |        ^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/redundant_async_block.fixed b/src/tools/clippy/tests/ui/redundant_async_block.fixed
new file mode 100644
index 00000000000..5f9931df45e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/redundant_async_block.fixed
@@ -0,0 +1,64 @@
+// run-rustfix
+
+#![allow(unused)]
+#![warn(clippy::redundant_async_block)]
+
+async fn func1(n: usize) -> usize {
+    n + 1
+}
+
+async fn func2() -> String {
+    let s = String::from("some string");
+    let f = async { (*s).to_owned() };
+    let x = f;
+    x.await
+}
+
+macro_rules! await_in_macro {
+    ($e:expr) => {
+        std::convert::identity($e).await
+    };
+}
+
+async fn func3(n: usize) -> usize {
+    // Do not lint (suggestion would be `std::convert::identity(func1(n))`
+    // which copies code from inside the macro)
+    async move { await_in_macro!(func1(n)) }.await
+}
+
+// This macro should never be linted as `$e` might contain `.await`
+macro_rules! async_await_parameter_in_macro {
+    ($e:expr) => {
+        async { $e.await }
+    };
+}
+
+// MISSED OPPORTUNITY: this macro could be linted as the `async` block does not
+// contain code coming from the parameters
+macro_rules! async_await_in_macro {
+    ($f:expr) => {
+        ($f)(async { func2().await })
+    };
+}
+
+fn main() {
+    let fut1 = async { 17 };
+    let fut2 = fut1;
+
+    let fut1 = async { 25 };
+    let fut2 = fut1;
+
+    let fut = async { 42 };
+
+    // Do not lint: not a single expression
+    let fut = async {
+        func1(10).await;
+        func2().await
+    };
+
+    // Do not lint: expression contains `.await`
+    let fut = async { func1(func2().await.len()).await };
+
+    let fut = async_await_parameter_in_macro!(func2());
+    let fut = async_await_in_macro!(std::convert::identity);
+}
diff --git a/src/tools/clippy/tests/ui/redundant_async_block.rs b/src/tools/clippy/tests/ui/redundant_async_block.rs
new file mode 100644
index 00000000000..de3c9970c65
--- /dev/null
+++ b/src/tools/clippy/tests/ui/redundant_async_block.rs
@@ -0,0 +1,64 @@
+// run-rustfix
+
+#![allow(unused)]
+#![warn(clippy::redundant_async_block)]
+
+async fn func1(n: usize) -> usize {
+    n + 1
+}
+
+async fn func2() -> String {
+    let s = String::from("some string");
+    let f = async { (*s).to_owned() };
+    let x = async { f.await };
+    x.await
+}
+
+macro_rules! await_in_macro {
+    ($e:expr) => {
+        std::convert::identity($e).await
+    };
+}
+
+async fn func3(n: usize) -> usize {
+    // Do not lint (suggestion would be `std::convert::identity(func1(n))`
+    // which copies code from inside the macro)
+    async move { await_in_macro!(func1(n)) }.await
+}
+
+// This macro should never be linted as `$e` might contain `.await`
+macro_rules! async_await_parameter_in_macro {
+    ($e:expr) => {
+        async { $e.await }
+    };
+}
+
+// MISSED OPPORTUNITY: this macro could be linted as the `async` block does not
+// contain code coming from the parameters
+macro_rules! async_await_in_macro {
+    ($f:expr) => {
+        ($f)(async { func2().await })
+    };
+}
+
+fn main() {
+    let fut1 = async { 17 };
+    let fut2 = async { fut1.await };
+
+    let fut1 = async { 25 };
+    let fut2 = async move { fut1.await };
+
+    let fut = async { async { 42 }.await };
+
+    // Do not lint: not a single expression
+    let fut = async {
+        func1(10).await;
+        func2().await
+    };
+
+    // Do not lint: expression contains `.await`
+    let fut = async { func1(func2().await.len()).await };
+
+    let fut = async_await_parameter_in_macro!(func2());
+    let fut = async_await_in_macro!(std::convert::identity);
+}
diff --git a/src/tools/clippy/tests/ui/redundant_async_block.stderr b/src/tools/clippy/tests/ui/redundant_async_block.stderr
new file mode 100644
index 00000000000..b16d96dce84
--- /dev/null
+++ b/src/tools/clippy/tests/ui/redundant_async_block.stderr
@@ -0,0 +1,28 @@
+error: this async expression only awaits a single future
+  --> $DIR/redundant_async_block.rs:13:13
+   |
+LL |     let x = async { f.await };
+   |             ^^^^^^^^^^^^^^^^^ help: you can reduce it to: `f`
+   |
+   = note: `-D clippy::redundant-async-block` implied by `-D warnings`
+
+error: this async expression only awaits a single future
+  --> $DIR/redundant_async_block.rs:46:16
+   |
+LL |     let fut2 = async { fut1.await };
+   |                ^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `fut1`
+
+error: this async expression only awaits a single future
+  --> $DIR/redundant_async_block.rs:49:16
+   |
+LL |     let fut2 = async move { fut1.await };
+   |                ^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `fut1`
+
+error: this async expression only awaits a single future
+  --> $DIR/redundant_async_block.rs:51:15
+   |
+LL |     let fut = async { async { 42 }.await };
+   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `async { 42 }`
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/redundant_closure_call_fixable.fixed b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.fixed
index c0e49ff4caa..b987fd2ce6f 100644
--- a/src/tools/clippy/tests/ui/redundant_closure_call_fixable.fixed
+++ b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.fixed
@@ -2,6 +2,7 @@
 
 #![feature(async_closure)]
 #![warn(clippy::redundant_closure_call)]
+#![allow(clippy::redundant_async_block)]
 #![allow(unused)]
 
 async fn something() -> u32 {
diff --git a/src/tools/clippy/tests/ui/redundant_closure_call_fixable.rs b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.rs
index 9e6e54348a8..633a2979d5d 100644
--- a/src/tools/clippy/tests/ui/redundant_closure_call_fixable.rs
+++ b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.rs
@@ -2,6 +2,7 @@
 
 #![feature(async_closure)]
 #![warn(clippy::redundant_closure_call)]
+#![allow(clippy::redundant_async_block)]
 #![allow(unused)]
 
 async fn something() -> u32 {
diff --git a/src/tools/clippy/tests/ui/redundant_closure_call_fixable.stderr b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.stderr
index d71bcba2a82..8a1f0771659 100644
--- a/src/tools/clippy/tests/ui/redundant_closure_call_fixable.stderr
+++ b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.stderr
@@ -1,5 +1,5 @@
 error: try not to call a closure in the expression where it is declared
-  --> $DIR/redundant_closure_call_fixable.rs:16:13
+  --> $DIR/redundant_closure_call_fixable.rs:17:13
    |
 LL |     let a = (|| 42)();
    |             ^^^^^^^^^ help: try doing something like: `42`
@@ -7,7 +7,7 @@ LL |     let a = (|| 42)();
    = note: `-D clippy::redundant-closure-call` implied by `-D warnings`
 
 error: try not to call a closure in the expression where it is declared
-  --> $DIR/redundant_closure_call_fixable.rs:17:13
+  --> $DIR/redundant_closure_call_fixable.rs:18:13
    |
 LL |       let b = (async || {
    |  _____________^
@@ -27,7 +27,7 @@ LL ~     };
    |
 
 error: try not to call a closure in the expression where it is declared
-  --> $DIR/redundant_closure_call_fixable.rs:22:13
+  --> $DIR/redundant_closure_call_fixable.rs:23:13
    |
 LL |       let c = (|| {
    |  _____________^
@@ -47,13 +47,13 @@ LL ~     };
    |
 
 error: try not to call a closure in the expression where it is declared
-  --> $DIR/redundant_closure_call_fixable.rs:27:13
+  --> $DIR/redundant_closure_call_fixable.rs:28:13
    |
 LL |     let d = (async || something().await)();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try doing something like: `async { something().await }`
 
 error: try not to call a closure in the expression where it is declared
-  --> $DIR/redundant_closure_call_fixable.rs:36:13
+  --> $DIR/redundant_closure_call_fixable.rs:37:13
    |
 LL |             (|| m!())()
    |             ^^^^^^^^^^^ help: try doing something like: `m!()`
@@ -64,7 +64,7 @@ LL |     m2!();
    = note: this error originates in the macro `m2` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: try not to call a closure in the expression where it is declared
-  --> $DIR/redundant_closure_call_fixable.rs:31:13
+  --> $DIR/redundant_closure_call_fixable.rs:32:13
    |
 LL |             (|| 0)()
    |             ^^^^^^^^ help: try doing something like: `0`
diff --git a/src/tools/clippy/tests/ui/swap.fixed b/src/tools/clippy/tests/ui/swap.fixed
index fa89706a815..04008c0d9b3 100644
--- a/src/tools/clippy/tests/ui/swap.fixed
+++ b/src/tools/clippy/tests/ui/swap.fixed
@@ -65,19 +65,19 @@ fn xor_swap_locals() {
     // This is an xor-based swap of local variables.
     let mut a = 0;
     let mut b = 1;
-    std::mem::swap(&mut a, &mut b)
+    std::mem::swap(&mut a, &mut b);
 }
 
 fn xor_field_swap() {
     // This is an xor-based swap of fields in a struct.
     let mut bar = Bar { a: 0, b: 1 };
-    std::mem::swap(&mut bar.a, &mut bar.b)
+    std::mem::swap(&mut bar.a, &mut bar.b);
 }
 
 fn xor_slice_swap() {
     // This is an xor-based swap of a slice
     let foo = &mut [1, 2];
-    foo.swap(0, 1)
+    foo.swap(0, 1);
 }
 
 fn xor_no_swap() {
diff --git a/src/tools/clippy/tests/ui/swap.stderr b/src/tools/clippy/tests/ui/swap.stderr
index f0acbfe253f..825c9261e19 100644
--- a/src/tools/clippy/tests/ui/swap.stderr
+++ b/src/tools/clippy/tests/ui/swap.stderr
@@ -4,7 +4,7 @@ error: this looks like you are swapping `bar.a` and `bar.b` manually
 LL | /     let temp = bar.a;
 LL | |     bar.a = bar.b;
 LL | |     bar.b = temp;
-   | |________________^ help: try: `std::mem::swap(&mut bar.a, &mut bar.b)`
+   | |_________________^ help: try: `std::mem::swap(&mut bar.a, &mut bar.b);`
    |
    = note: or maybe you should use `std::mem::replace`?
    = note: `-D clippy::manual-swap` implied by `-D warnings`
@@ -15,7 +15,7 @@ error: this looks like you are swapping elements of `foo` manually
 LL | /     let temp = foo[0];
 LL | |     foo[0] = foo[1];
 LL | |     foo[1] = temp;
-   | |_________________^ help: try: `foo.swap(0, 1)`
+   | |__________________^ help: try: `foo.swap(0, 1);`
 
 error: this looks like you are swapping elements of `foo` manually
   --> $DIR/swap.rs:46:5
@@ -23,7 +23,7 @@ error: this looks like you are swapping elements of `foo` manually
 LL | /     let temp = foo[0];
 LL | |     foo[0] = foo[1];
 LL | |     foo[1] = temp;
-   | |_________________^ help: try: `foo.swap(0, 1)`
+   | |__________________^ help: try: `foo.swap(0, 1);`
 
 error: this looks like you are swapping elements of `foo` manually
   --> $DIR/swap.rs:65:5
@@ -31,7 +31,7 @@ error: this looks like you are swapping elements of `foo` manually
 LL | /     let temp = foo[0];
 LL | |     foo[0] = foo[1];
 LL | |     foo[1] = temp;
-   | |_________________^ help: try: `foo.swap(0, 1)`
+   | |__________________^ help: try: `foo.swap(0, 1);`
 
 error: this looks like you are swapping `a` and `b` manually
   --> $DIR/swap.rs:76:5
@@ -39,7 +39,7 @@ error: this looks like you are swapping `a` and `b` manually
 LL | /     a ^= b;
 LL | |     b ^= a;
 LL | |     a ^= b;
-   | |___________^ help: try: `std::mem::swap(&mut a, &mut b)`
+   | |___________^ help: try: `std::mem::swap(&mut a, &mut b);`
 
 error: this looks like you are swapping `bar.a` and `bar.b` manually
   --> $DIR/swap.rs:84:5
@@ -47,7 +47,7 @@ error: this looks like you are swapping `bar.a` and `bar.b` manually
 LL | /     bar.a ^= bar.b;
 LL | |     bar.b ^= bar.a;
 LL | |     bar.a ^= bar.b;
-   | |___________________^ help: try: `std::mem::swap(&mut bar.a, &mut bar.b)`
+   | |___________________^ help: try: `std::mem::swap(&mut bar.a, &mut bar.b);`
 
 error: this looks like you are swapping elements of `foo` manually
   --> $DIR/swap.rs:92:5
@@ -55,7 +55,7 @@ error: this looks like you are swapping elements of `foo` manually
 LL | /     foo[0] ^= foo[1];
 LL | |     foo[1] ^= foo[0];
 LL | |     foo[0] ^= foo[1];
-   | |_____________________^ help: try: `foo.swap(0, 1)`
+   | |_____________________^ help: try: `foo.swap(0, 1);`
 
 error: this looks like you are swapping `foo[0][1]` and `bar[1][0]` manually
   --> $DIR/swap.rs:121:5
@@ -63,7 +63,7 @@ error: this looks like you are swapping `foo[0][1]` and `bar[1][0]` manually
 LL | /     let temp = foo[0][1];
 LL | |     foo[0][1] = bar[1][0];
 LL | |     bar[1][0] = temp;
-   | |____________________^ help: try: `std::mem::swap(&mut foo[0][1], &mut bar[1][0])`
+   | |_____________________^ help: try: `std::mem::swap(&mut foo[0][1], &mut bar[1][0]);`
    |
    = note: or maybe you should use `std::mem::replace`?
 
@@ -74,7 +74,7 @@ LL |       ; let t = a;
    |  _______^
 LL | |     a = b;
 LL | |     b = t;
-   | |_________^ help: try: `std::mem::swap(&mut a, &mut b)`
+   | |__________^ help: try: `std::mem::swap(&mut a, &mut b);`
    |
    = note: or maybe you should use `std::mem::replace`?
 
@@ -85,7 +85,7 @@ LL |       ; let t = c.0;
    |  _______^
 LL | |     c.0 = a;
 LL | |     a = t;
-   | |_________^ help: try: `std::mem::swap(&mut c.0, &mut a)`
+   | |__________^ help: try: `std::mem::swap(&mut c.0, &mut a);`
    |
    = note: or maybe you should use `std::mem::replace`?
 
@@ -95,7 +95,7 @@ error: this looks like you are swapping `b` and `a` manually
 LL | /     let t = b;
 LL | |     b = a;
 LL | |     a = t;
-   | |_________^ help: try: `std::mem::swap(&mut b, &mut a)`
+   | |__________^ help: try: `std::mem::swap(&mut b, &mut a);`
    |
    = note: or maybe you should use `std::mem::replace`?
 
@@ -151,7 +151,7 @@ error: this looks like you are swapping `s.0.x` and `s.0.y` manually
 LL | /     let t = s.0.x;
 LL | |     s.0.x = s.0.y;
 LL | |     s.0.y = t;
-   | |_____________^ help: try: `std::mem::swap(&mut s.0.x, &mut s.0.y)`
+   | |______________^ help: try: `std::mem::swap(&mut s.0.x, &mut s.0.y);`
    |
    = note: or maybe you should use `std::mem::replace`?
 
diff --git a/src/tools/clippy/tests/ui/trailing_empty_array.rs b/src/tools/clippy/tests/ui/trailing_empty_array.rs
index c39b0bcaf22..8e3749eef35 100644
--- a/src/tools/clippy/tests/ui/trailing_empty_array.rs
+++ b/src/tools/clippy/tests/ui/trailing_empty_array.rs
@@ -155,7 +155,6 @@ struct TupleStructReprC(i32, [usize; 0]);
 
 type NamedTuple = (i32, [usize; 0]);
 
-#[rustfmt::skip] // [rustfmt#4995](https://github.com/rust-lang/rustfmt/issues/4995)
 struct ConstParamZeroDefault<const N: usize = 0> {
     field: i32,
     last: [usize; N],
@@ -166,7 +165,6 @@ struct ConstParamNoDefault<const N: usize> {
     last: [usize; N],
 }
 
-#[rustfmt::skip] 
 struct ConstParamNonZeroDefault<const N: usize = 1> {
     field: i32,
     last: [usize; N],
diff --git a/src/tools/clippy/tests/ui/unnecessary_operation.fixed b/src/tools/clippy/tests/ui/unnecessary_operation.fixed
index d37163570ab..65d9c910b82 100644
--- a/src/tools/clippy/tests/ui/unnecessary_operation.fixed
+++ b/src/tools/clippy/tests/ui/unnecessary_operation.fixed
@@ -1,6 +1,5 @@
 // run-rustfix
 
-#![feature(box_syntax)]
 #![allow(clippy::deref_addrof, dead_code, unused, clippy::no_effect)]
 #![warn(clippy::unnecessary_operation)]
 
@@ -59,7 +58,6 @@ fn main() {
     5;6;get_number();
     get_number();
     get_number();
-    get_number();
     5;get_number();
     42;get_number();
     assert!([42, 55].len() > get_usize());
diff --git a/src/tools/clippy/tests/ui/unnecessary_operation.rs b/src/tools/clippy/tests/ui/unnecessary_operation.rs
index a14fd4bca0e..4e2acd59f04 100644
--- a/src/tools/clippy/tests/ui/unnecessary_operation.rs
+++ b/src/tools/clippy/tests/ui/unnecessary_operation.rs
@@ -1,6 +1,5 @@
 // run-rustfix
 
-#![feature(box_syntax)]
 #![allow(clippy::deref_addrof, dead_code, unused, clippy::no_effect)]
 #![warn(clippy::unnecessary_operation)]
 
@@ -57,7 +56,6 @@ fn main() {
     *&get_number();
     &get_number();
     (5, 6, get_number());
-    box get_number();
     get_number()..;
     ..get_number();
     5..get_number();
diff --git a/src/tools/clippy/tests/ui/unnecessary_operation.stderr b/src/tools/clippy/tests/ui/unnecessary_operation.stderr
index f66d08ecb82..44cf2e01ff7 100644
--- a/src/tools/clippy/tests/ui/unnecessary_operation.stderr
+++ b/src/tools/clippy/tests/ui/unnecessary_operation.stderr
@@ -1,5 +1,5 @@
 error: unnecessary operation
-  --> $DIR/unnecessary_operation.rs:51:5
+  --> $DIR/unnecessary_operation.rs:50:5
    |
 LL |     Tuple(get_number());
    |     ^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();`
@@ -7,109 +7,103 @@ LL |     Tuple(get_number());
    = note: `-D clippy::unnecessary-operation` implied by `-D warnings`
 
 error: unnecessary operation
-  --> $DIR/unnecessary_operation.rs:52:5
+  --> $DIR/unnecessary_operation.rs:51:5
    |
 LL |     Struct { field: get_number() };
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();`
 
 error: unnecessary operation
-  --> $DIR/unnecessary_operation.rs:53:5
+  --> $DIR/unnecessary_operation.rs:52:5
    |
 LL |     Struct { ..get_struct() };
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_struct();`
 
 error: unnecessary operation
-  --> $DIR/unnecessary_operation.rs:54:5
+  --> $DIR/unnecessary_operation.rs:53:5
    |
 LL |     Enum::Tuple(get_number());
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();`
 
 error: unnecessary operation
-  --> $DIR/unnecessary_operation.rs:55:5
+  --> $DIR/unnecessary_operation.rs:54:5
    |
 LL |     Enum::Struct { field: get_number() };
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();`
 
 error: unnecessary operation
-  --> $DIR/unnecessary_operation.rs:56:5
+  --> $DIR/unnecessary_operation.rs:55:5
    |
 LL |     5 + get_number();
    |     ^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `5;get_number();`
 
 error: unnecessary operation
-  --> $DIR/unnecessary_operation.rs:57:5
+  --> $DIR/unnecessary_operation.rs:56:5
    |
 LL |     *&get_number();
    |     ^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();`
 
 error: unnecessary operation
-  --> $DIR/unnecessary_operation.rs:58:5
+  --> $DIR/unnecessary_operation.rs:57:5
    |
 LL |     &get_number();
    |     ^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();`
 
 error: unnecessary operation
-  --> $DIR/unnecessary_operation.rs:59:5
+  --> $DIR/unnecessary_operation.rs:58:5
    |
 LL |     (5, 6, get_number());
    |     ^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `5;6;get_number();`
 
 error: unnecessary operation
-  --> $DIR/unnecessary_operation.rs:60:5
-   |
-LL |     box get_number();
-   |     ^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();`
-
-error: unnecessary operation
-  --> $DIR/unnecessary_operation.rs:61:5
+  --> $DIR/unnecessary_operation.rs:59:5
    |
 LL |     get_number()..;
    |     ^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();`
 
 error: unnecessary operation
-  --> $DIR/unnecessary_operation.rs:62:5
+  --> $DIR/unnecessary_operation.rs:60:5
    |
 LL |     ..get_number();
    |     ^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();`
 
 error: unnecessary operation
-  --> $DIR/unnecessary_operation.rs:63:5
+  --> $DIR/unnecessary_operation.rs:61:5
    |
 LL |     5..get_number();
    |     ^^^^^^^^^^^^^^^^ help: statement can be reduced to: `5;get_number();`
 
 error: unnecessary operation
-  --> $DIR/unnecessary_operation.rs:64:5
+  --> $DIR/unnecessary_operation.rs:62:5
    |
 LL |     [42, get_number()];
    |     ^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `42;get_number();`
 
 error: unnecessary operation
-  --> $DIR/unnecessary_operation.rs:65:5
+  --> $DIR/unnecessary_operation.rs:63:5
    |
 LL |     [42, 55][get_usize()];
    |     ^^^^^^^^^^^^^^^^^^^^^^ help: statement can be written as: `assert!([42, 55].len() > get_usize());`
 
 error: unnecessary operation
-  --> $DIR/unnecessary_operation.rs:66:5
+  --> $DIR/unnecessary_operation.rs:64:5
    |
 LL |     (42, get_number()).1;
    |     ^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `42;get_number();`
 
 error: unnecessary operation
-  --> $DIR/unnecessary_operation.rs:67:5
+  --> $DIR/unnecessary_operation.rs:65:5
    |
 LL |     [get_number(); 55];
    |     ^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();`
 
 error: unnecessary operation
-  --> $DIR/unnecessary_operation.rs:68:5
+  --> $DIR/unnecessary_operation.rs:66:5
    |
 LL |     [42; 55][get_usize()];
    |     ^^^^^^^^^^^^^^^^^^^^^^ help: statement can be written as: `assert!([42; 55].len() > get_usize());`
 
 error: unnecessary operation
-  --> $DIR/unnecessary_operation.rs:69:5
+  --> $DIR/unnecessary_operation.rs:67:5
    |
 LL | /     {
 LL | |         get_number()
@@ -117,12 +111,12 @@ LL | |     };
    | |______^ help: statement can be reduced to: `get_number();`
 
 error: unnecessary operation
-  --> $DIR/unnecessary_operation.rs:72:5
+  --> $DIR/unnecessary_operation.rs:70:5
    |
 LL | /     FooString {
 LL | |         s: String::from("blah"),
 LL | |     };
    | |______^ help: statement can be reduced to: `String::from("blah");`
 
-error: aborting due to 20 previous errors
+error: aborting due to 19 previous errors
 
diff --git a/src/tools/clippy/tests/ui/use_self.fixed b/src/tools/clippy/tests/ui/use_self.fixed
index 0a6166571eb..3ac6217312a 100644
--- a/src/tools/clippy/tests/ui/use_self.fixed
+++ b/src/tools/clippy/tests/ui/use_self.fixed
@@ -647,3 +647,13 @@ fn msrv_1_37() {
         }
     }
 }
+
+mod issue_10371 {
+    struct Val<const V: i32> {}
+
+    impl<const V: i32> From<Val<V>> for i32 {
+        fn from(_: Val<V>) -> Self {
+            todo!()
+        }
+    }
+}
diff --git a/src/tools/clippy/tests/ui/use_self.rs b/src/tools/clippy/tests/ui/use_self.rs
index 39c2b431f7f..9dc5d1e3f9b 100644
--- a/src/tools/clippy/tests/ui/use_self.rs
+++ b/src/tools/clippy/tests/ui/use_self.rs
@@ -647,3 +647,13 @@ fn msrv_1_37() {
         }
     }
 }
+
+mod issue_10371 {
+    struct Val<const V: i32> {}
+
+    impl<const V: i32> From<Val<V>> for i32 {
+        fn from(_: Val<V>) -> Self {
+            todo!()
+        }
+    }
+}
diff --git a/src/tools/collect-license-metadata/src/licenses.rs b/src/tools/collect-license-metadata/src/licenses.rs
index 1c95b1bc8e9..2855069db0d 100644
--- a/src/tools/collect-license-metadata/src/licenses.rs
+++ b/src/tools/collect-license-metadata/src/licenses.rs
@@ -42,6 +42,7 @@ pub(crate) struct License {
 impl License {
     fn simplify(&mut self) {
         self.remove_copyright_prefixes();
+        self.remove_trailing_dots();
         self.copyright.sort();
         self.copyright.dedup();
     }
@@ -62,4 +63,12 @@ impl License {
             *copyright = stripped.into();
         }
     }
+
+    fn remove_trailing_dots(&mut self) {
+        for copyright in &mut self.copyright {
+            if copyright.ends_with('.') {
+                *copyright = copyright.trim_end_matches('.').to_string();
+            }
+        }
+    }
 }
diff --git a/src/tools/collect-license-metadata/src/path_tree.rs b/src/tools/collect-license-metadata/src/path_tree.rs
index 133ff683737..7a2a440636d 100644
--- a/src/tools/collect-license-metadata/src/path_tree.rs
+++ b/src/tools/collect-license-metadata/src/path_tree.rs
@@ -13,7 +13,7 @@ pub(crate) enum Node<L> {
     Root { childs: Vec<Node<L>> },
     Directory { name: PathBuf, childs: Vec<Node<L>>, license: Option<L> },
     File { name: PathBuf, license: L },
-    FileGroup { names: Vec<PathBuf>, license: L },
+    Group { files: Vec<PathBuf>, directories: Vec<PathBuf>, license: L },
     Empty,
 }
 
@@ -22,7 +22,7 @@ impl Node<LicenseId> {
         self.merge_directories();
         self.collapse_in_licensed_directories();
         self.merge_directory_licenses();
-        self.merge_file_groups();
+        self.merge_groups();
         self.remove_empty();
     }
 
@@ -64,8 +64,8 @@ impl Node<LicenseId> {
                         Node::Root { .. } => {
                             panic!("can't have a root inside another element");
                         }
-                        Node::FileGroup { .. } => {
-                            panic!("FileGroup should not be present at this stage");
+                        Node::Group { .. } => {
+                            panic!("Group should not be present at this stage");
                         }
                         Node::Directory { license: Some(_), .. } => {
                             panic!("license should not be set at this stage");
@@ -86,8 +86,8 @@ impl Node<LicenseId> {
             }
             Node::Empty => {}
             Node::File { .. } => {}
-            Node::FileGroup { .. } => {
-                panic!("FileGroup should not be present at this stage");
+            Node::Group { .. } => {
+                panic!("Group should not be present at this stage");
             }
             Node::Directory { license: Some(_), .. } => {
                 panic!("license should not be set at this stage");
@@ -132,7 +132,7 @@ impl Node<LicenseId> {
                 }
             }
             Node::File { .. } => {}
-            Node::FileGroup { .. } => {}
+            Node::Group { .. } => panic!("group should not be present at this stage"),
             Node::Empty => {}
         }
     }
@@ -165,8 +165,8 @@ impl Node<LicenseId> {
                             Node::Root { .. } => {
                                 panic!("can't have a root inside another element");
                             }
-                            Node::FileGroup { .. } => {
-                                panic!("FileGroup should not be present at this stage");
+                            Node::Group { .. } => {
+                                panic!("Group should not be present at this stage");
                             }
                             Node::Directory { name: child_child_name, .. } => {
                                 *child_child_name = child_name.join(&child_child_name);
@@ -185,38 +185,74 @@ impl Node<LicenseId> {
             }
             Node::Empty => {}
             Node::File { .. } => {}
-            Node::FileGroup { .. } => {}
+            Node::Group { .. } => panic!("Group should not be present at this stage"),
         }
     }
 
     /// This pass groups multiple files in a directory with the same license into a single
-    /// "FileGroup", so that the license of all those files can be reported as a group.
+    /// "Group", so that the license of all those files can be reported as a group.
+    ///
+    /// This also merges directories *without exceptions*.
     ///
     /// Crucially this pass runs after collapse_in_licensed_directories, so the most common license
     /// will already be marked as the directory's license and won't be turned into a group.
-    fn merge_file_groups(&mut self) {
+    fn merge_groups(&mut self) {
+        #[derive(Default)]
+        struct Grouped {
+            files: Vec<PathBuf>,
+            directories: Vec<PathBuf>,
+        }
         match self {
             Node::Root { childs } | Node::Directory { childs, .. } => {
-                let mut grouped = BTreeMap::new();
+                let mut grouped: BTreeMap<LicenseId, Grouped> = BTreeMap::new();
 
                 for child in &mut *childs {
-                    child.merge_file_groups();
-                    if let Node::File { name, license } = child {
-                        grouped.entry(*license).or_insert_with(Vec::new).push(name.clone());
-                        *child = Node::Empty;
+                    child.merge_groups();
+                    match child {
+                        Node::Directory { name, childs, license: Some(license) } => {
+                            if childs.is_empty() {
+                                grouped
+                                    .entry(*license)
+                                    .or_insert_with(Grouped::default)
+                                    .directories
+                                    .push(name.clone());
+                                *child = Node::Empty;
+                            }
+                        }
+                        Node::File { name, license } => {
+                            grouped
+                                .entry(*license)
+                                .or_insert_with(Grouped::default)
+                                .files
+                                .push(name.clone());
+                            *child = Node::Empty;
+                        }
+                        _ => {}
                     }
                 }
 
-                for (license, mut names) in grouped.into_iter() {
-                    if names.len() == 1 {
-                        childs.push(Node::File { license, name: names.pop().unwrap() });
+                for (license, mut grouped) in grouped.into_iter() {
+                    if grouped.files.len() + grouped.directories.len() <= 1 {
+                        if let Some(name) = grouped.files.pop() {
+                            childs.push(Node::File { license, name });
+                        } else if let Some(name) = grouped.directories.pop() {
+                            childs.push(Node::Directory {
+                                name,
+                                childs: Vec::new(),
+                                license: Some(license),
+                            });
+                        }
                     } else {
-                        childs.push(Node::FileGroup { license, names });
+                        childs.push(Node::Group {
+                            license,
+                            files: grouped.files,
+                            directories: grouped.directories,
+                        });
                     }
                 }
             }
             Node::File { .. } => {}
-            Node::FileGroup { .. } => panic!("FileGroup should not be present at this stage"),
+            Node::Group { .. } => panic!("FileGroup should not be present at this stage"),
             Node::Empty => {}
         }
     }
@@ -231,7 +267,7 @@ impl Node<LicenseId> {
                 }
                 childs.retain(|child| !matches!(child, Node::Empty));
             }
-            Node::FileGroup { .. } => {}
+            Node::Group { .. } => {}
             Node::File { .. } => {}
             Node::Empty => {}
         }
@@ -278,16 +314,22 @@ pub(crate) fn expand_interned_licenses(
 ) -> Node<&License> {
     match node {
         Node::Root { childs } => Node::Root {
-            childs: childs.into_iter().map(|child| strip_interning(child, interner)).collect(),
+            childs: childs
+                .into_iter()
+                .map(|child| expand_interned_licenses(child, interner))
+                .collect(),
         },
         Node::Directory { name, childs, license } => Node::Directory {
-            childs: childs.into_iter().map(|child| strip_interning(child, interner)).collect(),
+            childs: childs
+                .into_iter()
+                .map(|child| expand_interned_licenses(child, interner))
+                .collect(),
             license: license.map(|license| interner.resolve(license)),
             name,
         },
         Node::File { name, license } => Node::File { name, license: interner.resolve(license) },
-        Node::FileGroup { names, license } => {
-            Node::FileGroup { names, license: interner.resolve(license) }
+        Node::Group { files, directories, license } => {
+            Node::Group { files, directories, license: interner.resolve(license) }
         }
         Node::Empty => Node::Empty,
     }
diff --git a/src/tools/generate-copyright/src/main.rs b/src/tools/generate-copyright/src/main.rs
index d172c9e157b..4d116c7da65 100644
--- a/src/tools/generate-copyright/src/main.rs
+++ b/src/tools/generate-copyright/src/main.rs
@@ -36,8 +36,8 @@ fn render_recursive(node: &Node, buffer: &mut Vec<u8>, depth: usize) -> Result<(
                 }
             }
         }
-        Node::FileGroup { names, license } => {
-            render_license(&prefix, names.iter(), license, buffer)?;
+        Node::Group { files, directories, license } => {
+            render_license(&prefix, directories.iter().chain(files.iter()), license, buffer)?;
         }
         Node::File { name, license } => {
             render_license(&prefix, std::iter::once(name), license, buffer)?;
@@ -76,7 +76,7 @@ pub(crate) enum Node {
     Root { childs: Vec<Node> },
     Directory { name: String, childs: Vec<Node>, license: License },
     File { name: String, license: License },
-    FileGroup { names: Vec<String>, license: License },
+    Group { files: Vec<String>, directories: Vec<String>, license: License },
 }
 
 #[derive(serde::Deserialize)]
diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version
index 301b322b285..0c6208f3ea7 100644
--- a/src/tools/miri/rust-version
+++ b/src/tools/miri/rust-version
@@ -1 +1 @@
-ff4b772f805ec1e1c1bd7e189ab8d5a4e3a6ef13
+1716932743a7b3705cbf0c34db0c4e070ed1930d
diff --git a/src/tools/miri/tests/fail/function_pointers/execute_memory.rs b/src/tools/miri/tests/fail/function_pointers/execute_memory.rs
index 967933e769b..89d8d22a9dd 100644
--- a/src/tools/miri/tests/fail/function_pointers/execute_memory.rs
+++ b/src/tools/miri/tests/fail/function_pointers/execute_memory.rs
@@ -1,10 +1,8 @@
 // Validation makes this fail in the wrong place
 //@compile-flags: -Zmiri-disable-validation
 
-#![feature(box_syntax)]
-
 fn main() {
-    let x = box 42;
+    let x = Box::new(42);
     unsafe {
         let f = std::mem::transmute::<Box<i32>, fn()>(x);
         f() //~ ERROR: function pointer but it does not point to a function
diff --git a/src/tools/miri/tests/pass/drop_empty_slice.rs b/src/tools/miri/tests/pass/drop_empty_slice.rs
index 9805ce0ace3..0413ed1fd0c 100644
--- a/src/tools/miri/tests/pass/drop_empty_slice.rs
+++ b/src/tools/miri/tests/pass/drop_empty_slice.rs
@@ -1,7 +1,5 @@
-#![feature(box_syntax)]
-
 fn main() {
     // With the nested Vec, this is calling Offset(Unique::empty(), 0) on drop.
     let args: Vec<Vec<i32>> = Vec::new();
-    let _val = box args;
+    let _val = Box::new(args);
 }
diff --git a/src/tools/miri/tests/pass/dst-struct.rs b/src/tools/miri/tests/pass/dst-struct.rs
index 7191068eb2c..59763bbbfdd 100644
--- a/src/tools/miri/tests/pass/dst-struct.rs
+++ b/src/tools/miri/tests/pass/dst-struct.rs
@@ -1,5 +1,3 @@
-#![feature(box_syntax)]
-
 struct Fat<T: ?Sized> {
     f1: isize,
     f2: &'static str,
@@ -109,7 +107,7 @@ pub fn main() {
     assert_eq!((*f2)[1], 2);
 
     // Nested Box.
-    let f1: Box<Fat<[isize; 3]>> = box Fat { f1: 5, f2: "some str", ptr: [1, 2, 3] };
+    let f1: Box<Fat<[isize; 3]>> = Box::new(Fat { f1: 5, f2: "some str", ptr: [1, 2, 3] });
     foo(&*f1);
     let f2: Box<Fat<[isize]>> = f1;
     foo(&*f2);
@@ -117,6 +115,6 @@ pub fn main() {
     let f3: Box<Fat<[isize]>> =
         Box::<Fat<[_; 3]>>::new(Fat { f1: 5, f2: "some str", ptr: [1, 2, 3] });
     foo(&*f3);
-    let f4: Box<Fat<[isize]>> = box Fat { f1: 5, f2: "some str", ptr: [1, 2, 3] };
+    let f4: Box<Fat<[isize]>> = Box::new(Fat { f1: 5, f2: "some str", ptr: [1, 2, 3] });
     foo(&*f4);
 }
diff --git a/src/tools/miri/tests/pass/heap.rs b/src/tools/miri/tests/pass/heap.rs
index 44537e74b5a..44e3a60cfe1 100644
--- a/src/tools/miri/tests/pass/heap.rs
+++ b/src/tools/miri/tests/pass/heap.rs
@@ -1,13 +1,7 @@
-#![feature(box_syntax)]
-
 fn make_box() -> Box<(i16, i16)> {
     Box::new((1, 2))
 }
 
-fn make_box_syntax() -> Box<(i16, i16)> {
-    box (1, 2)
-}
-
 fn allocate_reallocate() {
     let mut s = String::new();
 
@@ -29,6 +23,5 @@ fn allocate_reallocate() {
 
 fn main() {
     assert_eq!(*make_box(), (1, 2));
-    assert_eq!(*make_box_syntax(), (1, 2));
     allocate_reallocate();
 }
diff --git a/src/tools/miri/tests/pass/intrinsics-integer.rs b/src/tools/miri/tests/pass/intrinsics-integer.rs
index 546931f6ff8..13e7bd8e1b9 100644
--- a/src/tools/miri/tests/pass/intrinsics-integer.rs
+++ b/src/tools/miri/tests/pass/intrinsics-integer.rs
@@ -1,12 +1,5 @@
-// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
+// SPDX-License-Identifier: MIT OR Apache-2.0
+// SPDX-FileCopyrightText: The Rust Project Developers (see https://thanks.rust-lang.org)
 
 #![feature(core_intrinsics)]
 use std::intrinsics::*;
diff --git a/src/tools/miri/tests/pass/intrinsics-math.rs b/src/tools/miri/tests/pass/intrinsics-math.rs
index 5973f4cd197..9f2dc333f33 100644
--- a/src/tools/miri/tests/pass/intrinsics-math.rs
+++ b/src/tools/miri/tests/pass/intrinsics-math.rs
@@ -1,12 +1,5 @@
-// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
+// SPDX-License-Identifier: MIT OR Apache-2.0
+// SPDX-FileCopyrightText: The Rust Project Developers (see https://thanks.rust-lang.org)
 
 macro_rules! assert_approx_eq {
     ($a:expr, $b:expr) => {{
diff --git a/src/tools/miri/tests/pass/issues/issue-30530.rs b/src/tools/miri/tests/pass/issues/issue-30530.rs
index 472b42adaac..b50a43ffd83 100644
--- a/src/tools/miri/tests/pass/issues/issue-30530.rs
+++ b/src/tools/miri/tests/pass/issues/issue-30530.rs
@@ -1,12 +1,5 @@
-// Copyright 2012-2016 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
+// SPDX-License-Identifier: MIT OR Apache-2.0
+// SPDX-FileCopyrightText: The Rust Project Developers (see https://thanks.rust-lang.org)
 
 // Regression test for Issue #30530: alloca's created for storing
 // intermediate scratch values during brace-less match arms need to be
diff --git a/src/tools/miri/tests/pass/issues/issue-3794.rs b/src/tools/miri/tests/pass/issues/issue-3794.rs
index 5b5b22b5494..860d72bb586 100644
--- a/src/tools/miri/tests/pass/issues/issue-3794.rs
+++ b/src/tools/miri/tests/pass/issues/issue-3794.rs
@@ -1,5 +1,3 @@
-#![feature(box_syntax)]
-
 trait T {
     fn print(&self);
 }
@@ -25,7 +23,7 @@ fn print_s(s: &S) {
 }
 
 pub fn main() {
-    let s: Box<S> = box S { s: 5 };
+    let s: Box<S> = Box::new(S { s: 5 });
     print_s(&*s);
     let t: Box<dyn T> = s as Box<dyn T>;
     print_t(&*t);
diff --git a/src/tools/miri/tests/pass/move-arg-2-unique.rs b/src/tools/miri/tests/pass/move-arg-2-unique.rs
index 669602ac704..de21d67eb4f 100644
--- a/src/tools/miri/tests/pass/move-arg-2-unique.rs
+++ b/src/tools/miri/tests/pass/move-arg-2-unique.rs
@@ -1,11 +1,9 @@
-#![feature(box_syntax)]
-
 fn test(foo: Box<Vec<isize>>) {
     assert_eq!((*foo)[0], 10);
 }
 
 pub fn main() {
-    let x = box vec![10];
+    let x = Box::new(vec![10]);
     // Test forgetting a local by move-in
     test(x);
 }
diff --git a/src/tools/miri/tests/pass/move-arg-3-unique.rs b/src/tools/miri/tests/pass/move-arg-3-unique.rs
index 3b5c7cbbd42..6025481c32e 100644
--- a/src/tools/miri/tests/pass/move-arg-3-unique.rs
+++ b/src/tools/miri/tests/pass/move-arg-3-unique.rs
@@ -1,7 +1,5 @@
-#![feature(box_syntax)]
-
 pub fn main() {
-    let x = box 10;
+    let x = Box::new(10);
     let y = x;
     assert_eq!(*y, 10);
 }
diff --git a/src/tools/miri/tests/pass/mpsc.rs b/src/tools/miri/tests/pass/mpsc.rs
index 6e3c6e771cc..3824a0de907 100644
--- a/src/tools/miri/tests/pass/mpsc.rs
+++ b/src/tools/miri/tests/pass/mpsc.rs
@@ -1,15 +1,13 @@
-#![feature(box_syntax)]
-
 use std::sync::mpsc::channel;
 
 pub fn main() {
     let (tx, rx) = channel::<Box<_>>();
-    tx.send(box 100).unwrap();
+    tx.send(Box::new(100)).unwrap();
     let v = rx.recv().unwrap();
-    assert_eq!(v, box 100);
+    assert_eq!(v, Box::new(100));
 
-    tx.send(box 101).unwrap();
-    tx.send(box 102).unwrap();
-    assert_eq!(rx.recv().unwrap(), box 101);
-    assert_eq!(rx.recv().unwrap(), box 102);
+    tx.send(Box::new(101)).unwrap();
+    tx.send(Box::new(102)).unwrap();
+    assert_eq!(rx.recv().unwrap(), Box::new(101));
+    assert_eq!(rx.recv().unwrap(), Box::new(102));
 }
diff --git a/src/tools/miri/tests/pass/regions-lifetime-nonfree-late-bound.rs b/src/tools/miri/tests/pass/regions-lifetime-nonfree-late-bound.rs
index c91ac36ed6b..445dd43febb 100644
--- a/src/tools/miri/tests/pass/regions-lifetime-nonfree-late-bound.rs
+++ b/src/tools/miri/tests/pass/regions-lifetime-nonfree-late-bound.rs
@@ -12,8 +12,6 @@
 // doing region-folding, when really all clients of the region-folding
 // case only want to see *free* lifetime variables, not bound ones.
 
-#![feature(box_syntax)]
-
 pub fn main() {
     fn explicit() {
         fn test<F>(_x: Option<Box<F>>)
@@ -21,7 +19,7 @@ pub fn main() {
             F: FnMut(Box<dyn for<'a> FnMut(&'a isize)>),
         {
         }
-        test(Some(box |_f: Box<dyn for<'a> FnMut(&'a isize)>| {}));
+        test(Some(Box::new(|_f: Box<dyn for<'a> FnMut(&'a isize)>| {})));
     }
 
     // The code below is shorthand for the code above (and more likely
@@ -32,7 +30,7 @@ pub fn main() {
             F: FnMut(Box<dyn FnMut(&isize)>),
         {
         }
-        test(Some(box |_f: Box<dyn FnMut(&isize)>| {}));
+        test(Some(Box::new(|_f: Box<dyn FnMut(&isize)>| {})));
     }
 
     explicit();
diff --git a/src/tools/miri/tests/pass/tag-align-dyn-u64.rs b/src/tools/miri/tests/pass/tag-align-dyn-u64.rs
index 72211a8d3f3..81a43cc8bcc 100644
--- a/src/tools/miri/tests/pass/tag-align-dyn-u64.rs
+++ b/src/tools/miri/tests/pass/tag-align-dyn-u64.rs
@@ -1,12 +1,5 @@
-// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
+// SPDX-License-Identifier: MIT OR Apache-2.0
+// SPDX-FileCopyrightText: The Rust Project Developers (see https://thanks.rust-lang.org)
 
 use std::mem;
 
diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock
index ec197767259..fc77515b63b 100644
--- a/src/tools/rust-analyzer/Cargo.lock
+++ b/src/tools/rust-analyzer/Cargo.lock
@@ -572,6 +572,7 @@ dependencies = [
  "chalk-recursive",
  "chalk-solve",
  "cov-mark",
+ "either",
  "ena",
  "expect-test",
  "hir-def",
@@ -1714,6 +1715,7 @@ name = "syntax"
 version = "0.0.0"
 dependencies = [
  "cov-mark",
+ "either",
  "expect-test",
  "indexmap",
  "itertools",
diff --git a/src/tools/rust-analyzer/crates/flycheck/src/lib.rs b/src/tools/rust-analyzer/crates/flycheck/src/lib.rs
index 11f7b068ecb..accb14a51de 100644
--- a/src/tools/rust-analyzer/crates/flycheck/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/flycheck/src/lib.rs
@@ -76,7 +76,7 @@ impl fmt::Display for FlycheckConfig {
 #[derive(Debug)]
 pub struct FlycheckHandle {
     // XXX: drop order is significant
-    sender: Sender<Restart>,
+    sender: Sender<StateChange>,
     _thread: jod_thread::JoinHandle,
     id: usize,
 }
@@ -89,7 +89,7 @@ impl FlycheckHandle {
         workspace_root: AbsPathBuf,
     ) -> FlycheckHandle {
         let actor = FlycheckActor::new(id, sender, config, workspace_root);
-        let (sender, receiver) = unbounded::<Restart>();
+        let (sender, receiver) = unbounded::<StateChange>();
         let thread = jod_thread::Builder::new()
             .name("Flycheck".to_owned())
             .spawn(move || actor.run(receiver))
@@ -99,12 +99,12 @@ impl FlycheckHandle {
 
     /// Schedule a re-start of the cargo check worker.
     pub fn restart(&self) {
-        self.sender.send(Restart::Yes).unwrap();
+        self.sender.send(StateChange::Restart).unwrap();
     }
 
     /// Stop this cargo check worker.
     pub fn cancel(&self) {
-        self.sender.send(Restart::No).unwrap();
+        self.sender.send(StateChange::Cancel).unwrap();
     }
 
     pub fn id(&self) -> usize {
@@ -149,9 +149,9 @@ pub enum Progress {
     DidFailToRestart(String),
 }
 
-enum Restart {
-    Yes,
-    No,
+enum StateChange {
+    Restart,
+    Cancel,
 }
 
 /// A [`FlycheckActor`] is a single check instance of a workspace.
@@ -172,7 +172,7 @@ struct FlycheckActor {
 }
 
 enum Event {
-    Restart(Restart),
+    RequestStateChange(StateChange),
     CheckEvent(Option<CargoMessage>),
 }
 
@@ -191,30 +191,31 @@ impl FlycheckActor {
         self.send(Message::Progress { id: self.id, progress });
     }
 
-    fn next_event(&self, inbox: &Receiver<Restart>) -> Option<Event> {
+    fn next_event(&self, inbox: &Receiver<StateChange>) -> Option<Event> {
         let check_chan = self.cargo_handle.as_ref().map(|cargo| &cargo.receiver);
         if let Ok(msg) = inbox.try_recv() {
             // give restarts a preference so check outputs don't block a restart or stop
-            return Some(Event::Restart(msg));
+            return Some(Event::RequestStateChange(msg));
         }
         select! {
-            recv(inbox) -> msg => msg.ok().map(Event::Restart),
+            recv(inbox) -> msg => msg.ok().map(Event::RequestStateChange),
             recv(check_chan.unwrap_or(&never())) -> msg => Some(Event::CheckEvent(msg.ok())),
         }
     }
 
-    fn run(mut self, inbox: Receiver<Restart>) {
+    fn run(mut self, inbox: Receiver<StateChange>) {
         'event: while let Some(event) = self.next_event(&inbox) {
             match event {
-                Event::Restart(Restart::No) => {
+                Event::RequestStateChange(StateChange::Cancel) => {
+                    tracing::debug!(flycheck_id = self.id, "flycheck cancelled");
                     self.cancel_check_process();
                 }
-                Event::Restart(Restart::Yes) => {
+                Event::RequestStateChange(StateChange::Restart) => {
                     // Cancel the previously spawned process
                     self.cancel_check_process();
                     while let Ok(restart) = inbox.recv_timeout(Duration::from_millis(50)) {
                         // restart chained with a stop, so just cancel
-                        if let Restart::No = restart {
+                        if let StateChange::Cancel = restart {
                             continue 'event;
                         }
                     }
@@ -255,10 +256,20 @@ impl FlycheckActor {
                 }
                 Event::CheckEvent(Some(message)) => match message {
                     CargoMessage::CompilerArtifact(msg) => {
+                        tracing::trace!(
+                            flycheck_id = self.id,
+                            artifact = msg.target.name,
+                            "artifact received"
+                        );
                         self.report_progress(Progress::DidCheckCrate(msg.target.name));
                     }
 
                     CargoMessage::Diagnostic(msg) => {
+                        tracing::trace!(
+                            flycheck_id = self.id,
+                            message = msg.message,
+                            "diagnostic received"
+                        );
                         self.send(Message::AddDiagnostic {
                             id: self.id,
                             workspace_root: self.root.clone(),
@@ -445,42 +456,56 @@ impl CargoActor {
         // simply skip a line if it doesn't parse, which just ignores any
         // erroneous output.
 
-        let mut error = String::new();
-        let mut read_at_least_one_message = false;
+        let mut stdout_errors = String::new();
+        let mut stderr_errors = String::new();
+        let mut read_at_least_one_stdout_message = false;
+        let mut read_at_least_one_stderr_message = false;
+        let process_line = |line: &str, error: &mut String| {
+            // Try to deserialize a message from Cargo or Rustc.
+            let mut deserializer = serde_json::Deserializer::from_str(line);
+            deserializer.disable_recursion_limit();
+            if let Ok(message) = JsonMessage::deserialize(&mut deserializer) {
+                match message {
+                    // Skip certain kinds of messages to only spend time on what's useful
+                    JsonMessage::Cargo(message) => match message {
+                        cargo_metadata::Message::CompilerArtifact(artifact) if !artifact.fresh => {
+                            self.sender.send(CargoMessage::CompilerArtifact(artifact)).unwrap();
+                        }
+                        cargo_metadata::Message::CompilerMessage(msg) => {
+                            self.sender.send(CargoMessage::Diagnostic(msg.message)).unwrap();
+                        }
+                        _ => (),
+                    },
+                    JsonMessage::Rustc(message) => {
+                        self.sender.send(CargoMessage::Diagnostic(message)).unwrap();
+                    }
+                }
+                return true;
+            }
+
+            error.push_str(line);
+            error.push('\n');
+            return false;
+        };
         let output = streaming_output(
             self.stdout,
             self.stderr,
             &mut |line| {
-                read_at_least_one_message = true;
-
-                // Try to deserialize a message from Cargo or Rustc.
-                let mut deserializer = serde_json::Deserializer::from_str(line);
-                deserializer.disable_recursion_limit();
-                if let Ok(message) = JsonMessage::deserialize(&mut deserializer) {
-                    match message {
-                        // Skip certain kinds of messages to only spend time on what's useful
-                        JsonMessage::Cargo(message) => match message {
-                            cargo_metadata::Message::CompilerArtifact(artifact)
-                                if !artifact.fresh =>
-                            {
-                                self.sender.send(CargoMessage::CompilerArtifact(artifact)).unwrap();
-                            }
-                            cargo_metadata::Message::CompilerMessage(msg) => {
-                                self.sender.send(CargoMessage::Diagnostic(msg.message)).unwrap();
-                            }
-                            _ => (),
-                        },
-                        JsonMessage::Rustc(message) => {
-                            self.sender.send(CargoMessage::Diagnostic(message)).unwrap();
-                        }
-                    }
+                if process_line(line, &mut stdout_errors) {
+                    read_at_least_one_stdout_message = true;
                 }
             },
             &mut |line| {
-                error.push_str(line);
-                error.push('\n');
+                if process_line(line, &mut stderr_errors) {
+                    read_at_least_one_stderr_message = true;
+                }
             },
         );
+
+        let read_at_least_one_message =
+            read_at_least_one_stdout_message || read_at_least_one_stderr_message;
+        let mut error = stdout_errors;
+        error.push_str(&stderr_errors);
         match output {
             Ok(_) => Ok((read_at_least_one_message, error)),
             Err(e) => Err(io::Error::new(e.kind(), format!("{e:?}: {error}"))),
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/attr.rs b/src/tools/rust-analyzer/crates/hir-def/src/attr.rs
index fcd92ad3385..200072c172e 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/attr.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/attr.rs
@@ -300,6 +300,7 @@ impl AttrsWithOwner {
                 AdtId::UnionId(it) => attrs_from_item_tree(it.lookup(db).id, db),
             },
             AttrDefId::TraitId(it) => attrs_from_item_tree(it.lookup(db).id, db),
+            AttrDefId::TraitAliasId(it) => attrs_from_item_tree(it.lookup(db).id, db),
             AttrDefId::MacroId(it) => match it {
                 MacroId::Macro2Id(it) => attrs_from_item_tree(it.lookup(db).id, db),
                 MacroId::MacroRulesId(it) => attrs_from_item_tree(it.lookup(db).id, db),
@@ -315,26 +316,14 @@ impl AttrsWithOwner {
                     let src = it.parent().child_source(db);
                     RawAttrs::from_attrs_owner(
                         db.upcast(),
-                        src.with_value(src.value[it.local_id()].as_ref().either(
-                            |it| match it {
-                                ast::TypeOrConstParam::Type(it) => it as _,
-                                ast::TypeOrConstParam::Const(it) => it as _,
-                            },
-                            |it| it as _,
-                        )),
+                        src.with_value(&src.value[it.local_id()]),
                     )
                 }
                 GenericParamId::TypeParamId(it) => {
                     let src = it.parent().child_source(db);
                     RawAttrs::from_attrs_owner(
                         db.upcast(),
-                        src.with_value(src.value[it.local_id()].as_ref().either(
-                            |it| match it {
-                                ast::TypeOrConstParam::Type(it) => it as _,
-                                ast::TypeOrConstParam::Const(it) => it as _,
-                            },
-                            |it| it as _,
-                        )),
+                        src.with_value(&src.value[it.local_id()]),
                     )
                 }
                 GenericParamId::LifetimeParamId(it) => {
@@ -404,6 +393,7 @@ impl AttrsWithOwner {
             AttrDefId::StaticId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
             AttrDefId::ConstId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
             AttrDefId::TraitId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
+            AttrDefId::TraitAliasId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
             AttrDefId::TypeAliasId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
             AttrDefId::MacroId(id) => match id {
                 MacroId::Macro2Id(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
@@ -412,28 +402,14 @@ impl AttrsWithOwner {
             },
             AttrDefId::ImplId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
             AttrDefId::GenericParamId(id) => match id {
-                GenericParamId::ConstParamId(id) => {
-                    id.parent().child_source(db).map(|source| match &source[id.local_id()] {
-                        Either::Left(ast::TypeOrConstParam::Type(id)) => {
-                            ast::AnyHasAttrs::new(id.clone())
-                        }
-                        Either::Left(ast::TypeOrConstParam::Const(id)) => {
-                            ast::AnyHasAttrs::new(id.clone())
-                        }
-                        Either::Right(id) => ast::AnyHasAttrs::new(id.clone()),
-                    })
-                }
-                GenericParamId::TypeParamId(id) => {
-                    id.parent().child_source(db).map(|source| match &source[id.local_id()] {
-                        Either::Left(ast::TypeOrConstParam::Type(id)) => {
-                            ast::AnyHasAttrs::new(id.clone())
-                        }
-                        Either::Left(ast::TypeOrConstParam::Const(id)) => {
-                            ast::AnyHasAttrs::new(id.clone())
-                        }
-                        Either::Right(id) => ast::AnyHasAttrs::new(id.clone()),
-                    })
-                }
+                GenericParamId::ConstParamId(id) => id
+                    .parent()
+                    .child_source(db)
+                    .map(|source| ast::AnyHasAttrs::new(source[id.local_id()].clone())),
+                GenericParamId::TypeParamId(id) => id
+                    .parent()
+                    .child_source(db)
+                    .map(|source| ast::AnyHasAttrs::new(source[id.local_id()].clone())),
                 GenericParamId::LifetimeParamId(id) => id
                     .parent
                     .child_source(db)
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body.rs b/src/tools/rust-analyzer/crates/hir-def/src/body.rs
index 8fd9255b8b1..3be477d4877 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/body.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/body.rs
@@ -24,7 +24,7 @@ use syntax::{ast, AstPtr, SyntaxNode, SyntaxNodePtr};
 use crate::{
     attr::Attrs,
     db::DefDatabase,
-    expr::{dummy_expr_id, Expr, ExprId, Label, LabelId, Pat, PatId},
+    expr::{dummy_expr_id, Binding, BindingId, Expr, ExprId, Label, LabelId, Pat, PatId},
     item_scope::BuiltinShadowMode,
     macro_id_to_def_id,
     nameres::DefMap,
@@ -270,7 +270,7 @@ pub struct Mark {
 pub struct Body {
     pub exprs: Arena<Expr>,
     pub pats: Arena<Pat>,
-    pub or_pats: FxHashMap<PatId, Arc<[PatId]>>,
+    pub bindings: Arena<Binding>,
     pub labels: Arena<Label>,
     /// The patterns for the function's parameters. While the parameter types are
     /// part of the function signature, the patterns are not (they don't change
@@ -409,18 +409,6 @@ impl Body {
             .map(move |&block| (block, db.block_def_map(block).expect("block ID without DefMap")))
     }
 
-    pub fn pattern_representative(&self, pat: PatId) -> PatId {
-        self.or_pats.get(&pat).and_then(|pats| pats.first().copied()).unwrap_or(pat)
-    }
-
-    /// Retrieves all ident patterns this pattern shares the ident with.
-    pub fn ident_patterns_for<'slf>(&'slf self, pat: &'slf PatId) -> &'slf [PatId] {
-        match self.or_pats.get(pat) {
-            Some(pats) => pats,
-            None => std::slice::from_ref(pat),
-        }
-    }
-
     pub fn pretty_print(&self, db: &dyn DefDatabase, owner: DefWithBodyId) -> String {
         pretty::print_body_hir(db, self, owner)
     }
@@ -435,13 +423,14 @@ impl Body {
     }
 
     fn shrink_to_fit(&mut self) {
-        let Self { _c: _, body_expr: _, block_scopes, or_pats, exprs, labels, params, pats } = self;
+        let Self { _c: _, body_expr: _, block_scopes, exprs, labels, params, pats, bindings } =
+            self;
         block_scopes.shrink_to_fit();
-        or_pats.shrink_to_fit();
         exprs.shrink_to_fit();
         labels.shrink_to_fit();
         params.shrink_to_fit();
         pats.shrink_to_fit();
+        bindings.shrink_to_fit();
     }
 }
 
@@ -451,7 +440,7 @@ impl Default for Body {
             body_expr: dummy_expr_id(),
             exprs: Default::default(),
             pats: Default::default(),
-            or_pats: Default::default(),
+            bindings: Default::default(),
             labels: Default::default(),
             params: Default::default(),
             block_scopes: Default::default(),
@@ -484,6 +473,14 @@ impl Index<LabelId> for Body {
     }
 }
 
+impl Index<BindingId> for Body {
+    type Output = Binding;
+
+    fn index(&self, b: BindingId) -> &Binding {
+        &self.bindings[b]
+    }
+}
+
 // FIXME: Change `node_` prefix to something more reasonable.
 // Perhaps `expr_syntax` and `expr_id`?
 impl BodySourceMap {
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs
index 04b1c4f01e2..83ce9b6acbb 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs
@@ -15,6 +15,7 @@ use la_arena::Arena;
 use once_cell::unsync::OnceCell;
 use profile::Count;
 use rustc_hash::FxHashMap;
+use smallvec::SmallVec;
 use syntax::{
     ast::{
         self, ArrayExprKind, AstChildren, HasArgList, HasLoopBody, HasName, LiteralKind,
@@ -30,14 +31,14 @@ use crate::{
     builtin_type::{BuiltinFloat, BuiltinInt, BuiltinUint},
     db::DefDatabase,
     expr::{
-        dummy_expr_id, Array, BindingAnnotation, ClosureKind, Expr, ExprId, FloatTypeWrapper,
-        Label, LabelId, Literal, MatchArm, Movability, Pat, PatId, RecordFieldPat, RecordLitField,
-        Statement,
+        dummy_expr_id, Array, Binding, BindingAnnotation, BindingId, ClosureKind, Expr, ExprId,
+        FloatTypeWrapper, Label, LabelId, Literal, MatchArm, Movability, Pat, PatId,
+        RecordFieldPat, RecordLitField, Statement,
     },
     item_scope::BuiltinShadowMode,
     path::{GenericArgs, Path},
     type_ref::{Mutability, Rawness, TypeRef},
-    AdtId, BlockLoc, ModuleDefId, UnresolvedMacro,
+    AdtId, BlockId, BlockLoc, ModuleDefId, UnresolvedMacro,
 };
 
 pub struct LowerCtx<'a> {
@@ -87,16 +88,14 @@ pub(super) fn lower(
         body: Body {
             exprs: Arena::default(),
             pats: Arena::default(),
+            bindings: Arena::default(),
             labels: Arena::default(),
             params: Vec::new(),
             body_expr: dummy_expr_id(),
             block_scopes: Vec::new(),
             _c: Count::new(),
-            or_pats: Default::default(),
         },
         expander,
-        name_to_pat_grouping: Default::default(),
-        is_lowering_inside_or_pat: false,
         is_lowering_assignee_expr: false,
         is_lowering_generator: false,
     }
@@ -109,13 +108,26 @@ struct ExprCollector<'a> {
     ast_id_map: Arc<AstIdMap>,
     body: Body,
     source_map: BodySourceMap,
-    // a poor-mans union-find?
-    name_to_pat_grouping: FxHashMap<Name, Vec<PatId>>,
-    is_lowering_inside_or_pat: bool,
     is_lowering_assignee_expr: bool,
     is_lowering_generator: bool,
 }
 
+#[derive(Debug, Default)]
+struct BindingList {
+    map: FxHashMap<Name, BindingId>,
+}
+
+impl BindingList {
+    fn find(
+        &mut self,
+        ec: &mut ExprCollector<'_>,
+        name: Name,
+        mode: BindingAnnotation,
+    ) -> BindingId {
+        *self.map.entry(name).or_insert_with_key(|n| ec.alloc_binding(n.clone(), mode))
+    }
+}
+
 impl ExprCollector<'_> {
     fn collect(
         mut self,
@@ -127,17 +139,16 @@ impl ExprCollector<'_> {
                 param_list.self_param().filter(|_| attr_enabled.next().unwrap_or(false))
             {
                 let ptr = AstPtr::new(&self_param);
-                let param_pat = self.alloc_pat(
-                    Pat::Bind {
-                        name: name![self],
-                        mode: BindingAnnotation::new(
-                            self_param.mut_token().is_some() && self_param.amp_token().is_none(),
-                            false,
-                        ),
-                        subpat: None,
-                    },
-                    Either::Right(ptr),
+                let binding_id = self.alloc_binding(
+                    name![self],
+                    BindingAnnotation::new(
+                        self_param.mut_token().is_some() && self_param.amp_token().is_none(),
+                        false,
+                    ),
                 );
+                let param_pat =
+                    self.alloc_pat(Pat::Bind { id: binding_id, subpat: None }, Either::Right(ptr));
+                self.add_definition_to_binding(binding_id, param_pat);
                 self.body.params.push(param_pat);
             }
 
@@ -179,6 +190,9 @@ impl ExprCollector<'_> {
         id
     }
 
+    fn alloc_binding(&mut self, name: Name, mode: BindingAnnotation) -> BindingId {
+        self.body.bindings.alloc(Binding { name, mode, definitions: SmallVec::new() })
+    }
     fn alloc_pat(&mut self, pat: Pat, ptr: PatPtr) -> PatId {
         let src = self.expander.to_source(ptr);
         let id = self.make_pat(pat, src.clone());
@@ -238,33 +252,32 @@ impl ExprCollector<'_> {
             }
             ast::Expr::BlockExpr(e) => match e.modifier() {
                 Some(ast::BlockModifier::Try(_)) => {
-                    let body = self.collect_block(e);
-                    self.alloc_expr(Expr::TryBlock { body }, syntax_ptr)
+                    self.collect_block_(e, |id, statements, tail| Expr::TryBlock {
+                        id,
+                        statements,
+                        tail,
+                    })
                 }
                 Some(ast::BlockModifier::Unsafe(_)) => {
-                    let body = self.collect_block(e);
-                    self.alloc_expr(Expr::Unsafe { body }, syntax_ptr)
+                    self.collect_block_(e, |id, statements, tail| Expr::Unsafe {
+                        id,
+                        statements,
+                        tail,
+                    })
                 }
-                // FIXME: we need to record these effects somewhere...
                 Some(ast::BlockModifier::Label(label)) => {
                     let label = self.collect_label(label);
-                    let res = self.collect_block(e);
-                    match &mut self.body.exprs[res] {
-                        Expr::Block { label: block_label, .. } => {
-                            *block_label = Some(label);
-                        }
-                        _ => unreachable!(),
-                    }
-                    res
-                }
-                Some(ast::BlockModifier::Async(_)) => {
-                    let body = self.collect_block(e);
-                    self.alloc_expr(Expr::Async { body }, syntax_ptr)
-                }
-                Some(ast::BlockModifier::Const(_)) => {
-                    let body = self.collect_block(e);
-                    self.alloc_expr(Expr::Const { body }, syntax_ptr)
+                    self.collect_block_(e, |id, statements, tail| Expr::Block {
+                        id,
+                        statements,
+                        tail,
+                        label: Some(label),
+                    })
                 }
+                Some(ast::BlockModifier::Async(_)) => self
+                    .collect_block_(e, |id, statements, tail| Expr::Async { id, statements, tail }),
+                Some(ast::BlockModifier::Const(_)) => self
+                    .collect_block_(e, |id, statements, tail| Expr::Const { id, statements, tail }),
                 None => self.collect_block(e),
             },
             ast::Expr::LoopExpr(e) => {
@@ -737,6 +750,19 @@ impl ExprCollector<'_> {
     }
 
     fn collect_block(&mut self, block: ast::BlockExpr) -> ExprId {
+        self.collect_block_(block, |id, statements, tail| Expr::Block {
+            id,
+            statements,
+            tail,
+            label: None,
+        })
+    }
+
+    fn collect_block_(
+        &mut self,
+        block: ast::BlockExpr,
+        mk_block: impl FnOnce(BlockId, Box<[Statement]>, Option<ExprId>) -> Expr,
+    ) -> ExprId {
         let file_local_id = self.ast_id_map.ast_id(&block);
         let ast_id = AstId::new(self.expander.current_file_id, file_local_id);
         let block_loc =
@@ -769,15 +795,8 @@ impl ExprCollector<'_> {
         });
 
         let syntax_node_ptr = AstPtr::new(&block.into());
-        let expr_id = self.alloc_expr(
-            Expr::Block {
-                id: block_id,
-                statements: statements.into_boxed_slice(),
-                tail,
-                label: None,
-            },
-            syntax_node_ptr,
-        );
+        let expr_id = self
+            .alloc_expr(mk_block(block_id, statements.into_boxed_slice(), tail), syntax_node_ptr);
 
         self.expander.def_map = prev_def_map;
         self.expander.module = prev_local_module;
@@ -799,13 +818,7 @@ impl ExprCollector<'_> {
     }
 
     fn collect_pat(&mut self, pat: ast::Pat) -> PatId {
-        let pat_id = self.collect_pat_(pat);
-        for (_, pats) in self.name_to_pat_grouping.drain() {
-            let pats = Arc::<[_]>::from(pats);
-            self.body.or_pats.extend(pats.iter().map(|&pat| (pat, pats.clone())));
-        }
-        self.is_lowering_inside_or_pat = false;
-        pat_id
+        self.collect_pat_(pat, &mut BindingList::default())
     }
 
     fn collect_pat_opt(&mut self, pat: Option<ast::Pat>) -> PatId {
@@ -815,16 +828,18 @@ impl ExprCollector<'_> {
         }
     }
 
-    fn collect_pat_(&mut self, pat: ast::Pat) -> PatId {
+    fn collect_pat_(&mut self, pat: ast::Pat, binding_list: &mut BindingList) -> PatId {
         let pattern = match &pat {
             ast::Pat::IdentPat(bp) => {
                 let name = bp.name().map(|nr| nr.as_name()).unwrap_or_else(Name::missing);
 
-                let key = self.is_lowering_inside_or_pat.then(|| name.clone());
                 let annotation =
                     BindingAnnotation::new(bp.mut_token().is_some(), bp.ref_token().is_some());
-                let subpat = bp.pat().map(|subpat| self.collect_pat_(subpat));
-                let pattern = if annotation == BindingAnnotation::Unannotated && subpat.is_none() {
+                let subpat = bp.pat().map(|subpat| self.collect_pat_(subpat, binding_list));
+
+                let is_simple_ident_pat =
+                    annotation == BindingAnnotation::Unannotated && subpat.is_none();
+                let (binding, pattern) = if is_simple_ident_pat {
                     // This could also be a single-segment path pattern. To
                     // decide that, we need to try resolving the name.
                     let (resolved, _) = self.expander.def_map.resolve_path(
@@ -834,12 +849,12 @@ impl ExprCollector<'_> {
                         BuiltinShadowMode::Other,
                     );
                     match resolved.take_values() {
-                        Some(ModuleDefId::ConstId(_)) => Pat::Path(name.into()),
+                        Some(ModuleDefId::ConstId(_)) => (None, Pat::Path(name.into())),
                         Some(ModuleDefId::EnumVariantId(_)) => {
                             // this is only really valid for unit variants, but
                             // shadowing other enum variants with a pattern is
                             // an error anyway
-                            Pat::Path(name.into())
+                            (None, Pat::Path(name.into()))
                         }
                         Some(ModuleDefId::AdtId(AdtId::StructId(s)))
                             if self.db.struct_data(s).variant_data.kind() != StructKind::Record =>
@@ -847,30 +862,34 @@ impl ExprCollector<'_> {
                             // Funnily enough, record structs *can* be shadowed
                             // by pattern bindings (but unit or tuple structs
                             // can't).
-                            Pat::Path(name.into())
+                            (None, Pat::Path(name.into()))
                         }
                         // shadowing statics is an error as well, so we just ignore that case here
-                        _ => Pat::Bind { name, mode: annotation, subpat },
+                        _ => {
+                            let id = binding_list.find(self, name, annotation);
+                            (Some(id), Pat::Bind { id, subpat })
+                        }
                     }
                 } else {
-                    Pat::Bind { name, mode: annotation, subpat }
+                    let id = binding_list.find(self, name, annotation);
+                    (Some(id), Pat::Bind { id, subpat })
                 };
 
                 let ptr = AstPtr::new(&pat);
                 let pat = self.alloc_pat(pattern, Either::Left(ptr));
-                if let Some(key) = key {
-                    self.name_to_pat_grouping.entry(key).or_default().push(pat);
+                if let Some(binding_id) = binding {
+                    self.add_definition_to_binding(binding_id, pat);
                 }
                 return pat;
             }
             ast::Pat::TupleStructPat(p) => {
                 let path =
                     p.path().and_then(|path| self.expander.parse_path(self.db, path)).map(Box::new);
-                let (args, ellipsis) = self.collect_tuple_pat(p.fields());
+                let (args, ellipsis) = self.collect_tuple_pat(p.fields(), binding_list);
                 Pat::TupleStruct { path, args, ellipsis }
             }
             ast::Pat::RefPat(p) => {
-                let pat = self.collect_pat_opt(p.pat());
+                let pat = self.collect_pat_opt_(p.pat(), binding_list);
                 let mutability = Mutability::from_mutable(p.mut_token().is_some());
                 Pat::Ref { pat, mutability }
             }
@@ -880,13 +899,12 @@ impl ExprCollector<'_> {
                 path.map(Pat::Path).unwrap_or(Pat::Missing)
             }
             ast::Pat::OrPat(p) => {
-                self.is_lowering_inside_or_pat = true;
-                let pats = p.pats().map(|p| self.collect_pat_(p)).collect();
+                let pats = p.pats().map(|p| self.collect_pat_(p, binding_list)).collect();
                 Pat::Or(pats)
             }
-            ast::Pat::ParenPat(p) => return self.collect_pat_opt_(p.pat()),
+            ast::Pat::ParenPat(p) => return self.collect_pat_opt_(p.pat(), binding_list),
             ast::Pat::TuplePat(p) => {
-                let (args, ellipsis) = self.collect_tuple_pat(p.fields());
+                let (args, ellipsis) = self.collect_tuple_pat(p.fields(), binding_list);
                 Pat::Tuple { args, ellipsis }
             }
             ast::Pat::WildcardPat(_) => Pat::Wild,
@@ -899,7 +917,7 @@ impl ExprCollector<'_> {
                     .fields()
                     .filter_map(|f| {
                         let ast_pat = f.pat()?;
-                        let pat = self.collect_pat_(ast_pat);
+                        let pat = self.collect_pat_(ast_pat, binding_list);
                         let name = f.field_name()?.as_name();
                         Some(RecordFieldPat { name, pat })
                     })
@@ -918,9 +936,15 @@ impl ExprCollector<'_> {
 
                 // FIXME properly handle `RestPat`
                 Pat::Slice {
-                    prefix: prefix.into_iter().map(|p| self.collect_pat_(p)).collect(),
-                    slice: slice.map(|p| self.collect_pat_(p)),
-                    suffix: suffix.into_iter().map(|p| self.collect_pat_(p)).collect(),
+                    prefix: prefix
+                        .into_iter()
+                        .map(|p| self.collect_pat_(p, binding_list))
+                        .collect(),
+                    slice: slice.map(|p| self.collect_pat_(p, binding_list)),
+                    suffix: suffix
+                        .into_iter()
+                        .map(|p| self.collect_pat_(p, binding_list))
+                        .collect(),
                 }
             }
             ast::Pat::LiteralPat(lit) => {
@@ -943,7 +967,7 @@ impl ExprCollector<'_> {
                 Pat::Missing
             }
             ast::Pat::BoxPat(boxpat) => {
-                let inner = self.collect_pat_opt_(boxpat.pat());
+                let inner = self.collect_pat_opt_(boxpat.pat(), binding_list);
                 Pat::Box { inner }
             }
             ast::Pat::ConstBlockPat(const_block_pat) => {
@@ -960,7 +984,7 @@ impl ExprCollector<'_> {
                     let src = self.expander.to_source(Either::Left(AstPtr::new(&pat)));
                     let pat =
                         self.collect_macro_call(call, macro_ptr, true, |this, expanded_pat| {
-                            this.collect_pat_opt_(expanded_pat)
+                            this.collect_pat_opt_(expanded_pat, binding_list)
                         });
                     self.source_map.pat_map.insert(src, pat);
                     return pat;
@@ -974,21 +998,25 @@ impl ExprCollector<'_> {
         self.alloc_pat(pattern, Either::Left(ptr))
     }
 
-    fn collect_pat_opt_(&mut self, pat: Option<ast::Pat>) -> PatId {
+    fn collect_pat_opt_(&mut self, pat: Option<ast::Pat>, binding_list: &mut BindingList) -> PatId {
         match pat {
-            Some(pat) => self.collect_pat_(pat),
+            Some(pat) => self.collect_pat_(pat, binding_list),
             None => self.missing_pat(),
         }
     }
 
-    fn collect_tuple_pat(&mut self, args: AstChildren<ast::Pat>) -> (Box<[PatId]>, Option<usize>) {
+    fn collect_tuple_pat(
+        &mut self,
+        args: AstChildren<ast::Pat>,
+        binding_list: &mut BindingList,
+    ) -> (Box<[PatId]>, Option<usize>) {
         // Find the location of the `..`, if there is one. Note that we do not
         // consider the possibility of there being multiple `..` here.
         let ellipsis = args.clone().position(|p| matches!(p, ast::Pat::RestPat(_)));
         // We want to skip the `..` pattern here, since we account for it above.
         let args = args
             .filter(|p| !matches!(p, ast::Pat::RestPat(_)))
-            .map(|p| self.collect_pat_(p))
+            .map(|p| self.collect_pat_(p, binding_list))
             .collect();
 
         (args, ellipsis)
@@ -1017,6 +1045,10 @@ impl ExprCollector<'_> {
             None => Some(()),
         }
     }
+
+    fn add_definition_to_binding(&mut self, binding_id: BindingId, pat_id: PatId) {
+        self.body.bindings[binding_id].definitions.push(pat_id);
+    }
 }
 
 impl From<ast::LiteralKind> for Literal {
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs
index 4b4664a1cf4..f8b159797e4 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs
@@ -5,7 +5,7 @@ use std::fmt::{self, Write};
 use syntax::ast::HasName;
 
 use crate::{
-    expr::{Array, BindingAnnotation, ClosureKind, Literal, Movability, Statement},
+    expr::{Array, BindingAnnotation, BindingId, ClosureKind, Literal, Movability, Statement},
     pretty::{print_generic_args, print_path, print_type_ref},
     type_ref::TypeRef,
 };
@@ -292,18 +292,6 @@ impl<'a> Printer<'a> {
                 self.print_expr(*expr);
                 w!(self, "?");
             }
-            Expr::TryBlock { body } => {
-                w!(self, "try ");
-                self.print_expr(*body);
-            }
-            Expr::Async { body } => {
-                w!(self, "async ");
-                self.print_expr(*body);
-            }
-            Expr::Const { body } => {
-                w!(self, "const ");
-                self.print_expr(*body);
-            }
             Expr::Cast { expr, type_ref } => {
                 self.print_expr(*expr);
                 w!(self, " as ");
@@ -402,10 +390,6 @@ impl<'a> Printer<'a> {
                 }
                 w!(self, ")");
             }
-            Expr::Unsafe { body } => {
-                w!(self, "unsafe ");
-                self.print_expr(*body);
-            }
             Expr::Array(arr) => {
                 w!(self, "[");
                 if !matches!(arr, Array::ElementList { elements, .. } if elements.is_empty()) {
@@ -428,25 +412,47 @@ impl<'a> Printer<'a> {
             }
             Expr::Literal(lit) => self.print_literal(lit),
             Expr::Block { id: _, statements, tail, label } => {
-                self.whitespace();
-                if let Some(lbl) = label {
-                    w!(self, "{}: ", self.body[*lbl].name);
+                let label = label.map(|lbl| format!("{}: ", self.body[lbl].name));
+                self.print_block(label.as_deref(), statements, tail);
+            }
+            Expr::Unsafe { id: _, statements, tail } => {
+                self.print_block(Some("unsafe "), statements, tail);
+            }
+            Expr::TryBlock { id: _, statements, tail } => {
+                self.print_block(Some("try "), statements, tail);
+            }
+            Expr::Async { id: _, statements, tail } => {
+                self.print_block(Some("async "), statements, tail);
+            }
+            Expr::Const { id: _, statements, tail } => {
+                self.print_block(Some("const "), statements, tail);
+            }
+        }
+    }
+
+    fn print_block(
+        &mut self,
+        label: Option<&str>,
+        statements: &Box<[Statement]>,
+        tail: &Option<la_arena::Idx<Expr>>,
+    ) {
+        self.whitespace();
+        if let Some(lbl) = label {
+            w!(self, "{}", lbl);
+        }
+        w!(self, "{{");
+        if !statements.is_empty() || tail.is_some() {
+            self.indented(|p| {
+                for stmt in &**statements {
+                    p.print_stmt(stmt);
                 }
-                w!(self, "{{");
-                if !statements.is_empty() || tail.is_some() {
-                    self.indented(|p| {
-                        for stmt in &**statements {
-                            p.print_stmt(stmt);
-                        }
-                        if let Some(tail) = tail {
-                            p.print_expr(*tail);
-                        }
-                        p.newline();
-                    });
+                if let Some(tail) = tail {
+                    p.print_expr(*tail);
                 }
-                w!(self, "}}");
-            }
+                p.newline();
+            });
         }
+        w!(self, "}}");
     }
 
     fn print_pat(&mut self, pat: PatId) {
@@ -518,14 +524,8 @@ impl<'a> Printer<'a> {
             }
             Pat::Path(path) => self.print_path(path),
             Pat::Lit(expr) => self.print_expr(*expr),
-            Pat::Bind { mode, name, subpat } => {
-                let mode = match mode {
-                    BindingAnnotation::Unannotated => "",
-                    BindingAnnotation::Mutable => "mut ",
-                    BindingAnnotation::Ref => "ref ",
-                    BindingAnnotation::RefMut => "ref mut ",
-                };
-                w!(self, "{}{}", mode, name);
+            Pat::Bind { id, subpat } => {
+                self.print_binding(*id);
                 if let Some(pat) = subpat {
                     self.whitespace();
                     self.print_pat(*pat);
@@ -629,4 +629,15 @@ impl<'a> Printer<'a> {
     fn print_path(&mut self, path: &Path) {
         print_path(path, self).unwrap();
     }
+
+    fn print_binding(&mut self, id: BindingId) {
+        let Binding { name, mode, .. } = &self.body.bindings[id];
+        let mode = match mode {
+            BindingAnnotation::Unannotated => "",
+            BindingAnnotation::Mutable => "mut ",
+            BindingAnnotation::Ref => "ref ",
+            BindingAnnotation::RefMut => "ref mut ",
+        };
+        w!(self, "{}{}", mode, name);
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/scope.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/scope.rs
index 2617d4288a3..12fc1f116d7 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/body/scope.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/body/scope.rs
@@ -8,7 +8,7 @@ use rustc_hash::FxHashMap;
 use crate::{
     body::Body,
     db::DefDatabase,
-    expr::{Expr, ExprId, LabelId, Pat, PatId, Statement},
+    expr::{Binding, BindingId, Expr, ExprId, LabelId, Pat, PatId, Statement},
     BlockId, DefWithBodyId,
 };
 
@@ -23,7 +23,7 @@ pub struct ExprScopes {
 #[derive(Debug, PartialEq, Eq)]
 pub struct ScopeEntry {
     name: Name,
-    pat: PatId,
+    binding: BindingId,
 }
 
 impl ScopeEntry {
@@ -31,8 +31,8 @@ impl ScopeEntry {
         &self.name
     }
 
-    pub fn pat(&self) -> PatId {
-        self.pat
+    pub fn binding(&self) -> BindingId {
+        self.binding
     }
 }
 
@@ -66,6 +66,7 @@ impl ExprScopes {
         self.scopes[scope].label.clone()
     }
 
+    /// Returns the scopes in ascending order.
     pub fn scope_chain(&self, scope: Option<ScopeId>) -> impl Iterator<Item = ScopeId> + '_ {
         std::iter::successors(scope, move |&scope| self.scopes[scope].parent)
     }
@@ -125,18 +126,23 @@ impl ExprScopes {
         })
     }
 
-    fn add_bindings(&mut self, body: &Body, scope: ScopeId, pat: PatId) {
+    fn add_bindings(&mut self, body: &Body, scope: ScopeId, binding: BindingId) {
+        let Binding { name, .. } = &body.bindings[binding];
+        let entry = ScopeEntry { name: name.clone(), binding };
+        self.scopes[scope].entries.push(entry);
+    }
+
+    fn add_pat_bindings(&mut self, body: &Body, scope: ScopeId, pat: PatId) {
         let pattern = &body[pat];
-        if let Pat::Bind { name, .. } = pattern {
-            let entry = ScopeEntry { name: name.clone(), pat };
-            self.scopes[scope].entries.push(entry);
+        if let Pat::Bind { id, .. } = pattern {
+            self.add_bindings(body, scope, *id);
         }
 
-        pattern.walk_child_pats(|pat| self.add_bindings(body, scope, pat));
+        pattern.walk_child_pats(|pat| self.add_pat_bindings(body, scope, pat));
     }
 
     fn add_params_bindings(&mut self, body: &Body, scope: ScopeId, params: &[PatId]) {
-        params.iter().for_each(|pat| self.add_bindings(body, scope, *pat));
+        params.iter().for_each(|pat| self.add_pat_bindings(body, scope, *pat));
     }
 
     fn set_scope(&mut self, node: ExprId, scope: ScopeId) {
@@ -169,7 +175,7 @@ fn compute_block_scopes(
                 }
 
                 *scope = scopes.new_scope(*scope);
-                scopes.add_bindings(body, *scope, *pat);
+                scopes.add_pat_bindings(body, *scope, *pat);
             }
             Statement::Expr { expr, .. } => {
                 compute_expr_scopes(*expr, body, scopes, scope);
@@ -194,10 +200,20 @@ fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope
             scopes.set_scope(expr, scope);
             compute_block_scopes(statements, *tail, body, scopes, &mut scope);
         }
+        Expr::Unsafe { id, statements, tail }
+        | Expr::Async { id, statements, tail }
+        | Expr::Const { id, statements, tail }
+        | Expr::TryBlock { id, statements, tail } => {
+            let mut scope = scopes.new_block_scope(*scope, *id, None);
+            // Overwrite the old scope for the block expr, so that every block scope can be found
+            // via the block itself (important for blocks that only contain items, no expressions).
+            scopes.set_scope(expr, scope);
+            compute_block_scopes(statements, *tail, body, scopes, &mut scope);
+        }
         Expr::For { iterable, pat, body: body_expr, label } => {
             compute_expr_scopes(*iterable, body, scopes, scope);
             let mut scope = scopes.new_labeled_scope(*scope, make_label(label));
-            scopes.add_bindings(body, scope, *pat);
+            scopes.add_pat_bindings(body, scope, *pat);
             compute_expr_scopes(*body_expr, body, scopes, &mut scope);
         }
         Expr::While { condition, body: body_expr, label } => {
@@ -218,7 +234,7 @@ fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope
             compute_expr_scopes(*expr, body, scopes, scope);
             for arm in arms.iter() {
                 let mut scope = scopes.new_scope(*scope);
-                scopes.add_bindings(body, scope, arm.pat);
+                scopes.add_pat_bindings(body, scope, arm.pat);
                 if let Some(guard) = arm.guard {
                     scope = scopes.new_scope(scope);
                     compute_expr_scopes(guard, body, scopes, &mut scope);
@@ -237,7 +253,7 @@ fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope
         &Expr::Let { pat, expr } => {
             compute_expr_scopes(expr, body, scopes, scope);
             *scope = scopes.new_scope(*scope);
-            scopes.add_bindings(body, *scope, pat);
+            scopes.add_pat_bindings(body, *scope, pat);
         }
         e => e.walk_child_exprs(|e| compute_expr_scopes(e, body, scopes, scope)),
     };
@@ -439,7 +455,7 @@ fn foo() {
         let function = find_function(&db, file_id);
 
         let scopes = db.expr_scopes(function.into());
-        let (_body, source_map) = db.body_with_source_map(function.into());
+        let (body, source_map) = db.body_with_source_map(function.into());
 
         let expr_scope = {
             let expr_ast = name_ref.syntax().ancestors().find_map(ast::Expr::cast).unwrap();
@@ -449,7 +465,9 @@ fn foo() {
         };
 
         let resolved = scopes.resolve_name_in_scope(expr_scope, &name_ref.as_name()).unwrap();
-        let pat_src = source_map.pat_syntax(resolved.pat()).unwrap();
+        let pat_src = source_map
+            .pat_syntax(*body.bindings[resolved.binding()].definitions.first().unwrap())
+            .unwrap();
 
         let local_name = pat_src.value.either(
             |it| it.syntax_node_ptr().to_node(file.syntax()),
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/tests/block.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/tests/block.rs
index 3bba08cfcce..77ac221e590 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/body/tests/block.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/body/tests/block.rs
@@ -395,3 +395,25 @@ fn foo() {
         "#]],
     )
 }
+
+#[test]
+fn trailing_expr_macro_expands_stmts() {
+    check_at(
+        r#"
+macro_rules! foo {
+    () => { const FOO: u32 = 0;const BAR: u32 = 0; };
+}
+fn f() {$0
+    foo!{}
+};
+        "#,
+        expect![[r#"
+            block scope
+            BAR: v
+            FOO: v
+
+            crate
+            f: v
+        "#]],
+    )
+}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/child_by_source.rs b/src/tools/rust-analyzer/crates/hir-def/src/child_by_source.rs
index 19d2fe956f0..68b57acca2a 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/child_by_source.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/child_by_source.rs
@@ -1,7 +1,7 @@
 //! When *constructing* `hir`, we start at some parent syntax node and recursively
 //! lower the children.
 //!
-//! This modules allows one to go in the opposite direction: start with a syntax
+//! This module allows one to go in the opposite direction: start with a syntax
 //! node for a *child*, and get its hir.
 
 use either::Either;
@@ -145,6 +145,7 @@ impl ChildBySource for ItemScope {
                 ModuleDefId::StaticId(id) => insert!(map[keys::STATIC].insert(id)),
                 ModuleDefId::TypeAliasId(id) => insert!(map[keys::TYPE_ALIAS].insert(id)),
                 ModuleDefId::TraitId(id) => insert!(map[keys::TRAIT].insert(id)),
+                ModuleDefId::TraitAliasId(id) => insert!(map[keys::TRAIT_ALIAS].insert(id)),
                 ModuleDefId::AdtId(adt) => match adt {
                     AdtId::StructId(id) => insert!(map[keys::STRUCT].insert(id)),
                     AdtId::UnionId(id) => insert!(map[keys::UNION].insert(id)),
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/data.rs b/src/tools/rust-analyzer/crates/hir-def/src/data.rs
index c3c1dfd39ac..ee6e269fe55 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/data.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/data.rs
@@ -22,7 +22,7 @@ use crate::{
     visibility::RawVisibility,
     AssocItemId, AstIdWithPath, ConstId, ConstLoc, FunctionId, FunctionLoc, HasModule, ImplId,
     Intern, ItemContainerId, ItemLoc, Lookup, Macro2Id, MacroRulesId, ModuleId, ProcMacroId,
-    StaticId, TraitId, TypeAliasId, TypeAliasLoc,
+    StaticId, TraitAliasId, TraitId, TypeAliasId, TypeAliasLoc,
 };
 
 #[derive(Debug, Clone, PartialEq, Eq)]
@@ -245,19 +245,11 @@ impl TraitData {
             attrs.by_key("rustc_skip_array_during_method_dispatch").exists();
         let rustc_has_incoherent_inherent_impls =
             attrs.by_key("rustc_has_incoherent_inherent_impls").exists();
-        let (items, attribute_calls, diagnostics) = match &tr_def.items {
-            Some(items) => {
-                let mut collector = AssocItemCollector::new(
-                    db,
-                    module_id,
-                    tree_id.file_id(),
-                    ItemContainerId::TraitId(tr),
-                );
-                collector.collect(&item_tree, tree_id.tree_id(), items);
-                collector.finish()
-            }
-            None => Default::default(),
-        };
+        let mut collector =
+            AssocItemCollector::new(db, module_id, tree_id.file_id(), ItemContainerId::TraitId(tr));
+        collector.collect(&item_tree, tree_id.tree_id(), &tr_def.items);
+        let (items, attribute_calls, diagnostics) = collector.finish();
+
         (
             Arc::new(TraitData {
                 name,
@@ -300,6 +292,23 @@ impl TraitData {
 }
 
 #[derive(Debug, Clone, PartialEq, Eq)]
+pub struct TraitAliasData {
+    pub name: Name,
+    pub visibility: RawVisibility,
+}
+
+impl TraitAliasData {
+    pub(crate) fn trait_alias_query(db: &dyn DefDatabase, id: TraitAliasId) -> Arc<TraitAliasData> {
+        let loc = id.lookup(db);
+        let item_tree = loc.id.item_tree(db);
+        let alias = &item_tree[loc.id.value];
+        let visibility = item_tree[alias.visibility].clone();
+
+        Arc::new(TraitAliasData { name: alias.name.clone(), visibility })
+    }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
 pub struct ImplData {
     pub target_trait: Option<Interned<TraitRef>>,
     pub self_ty: Interned<TypeRef>,
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/db.rs b/src/tools/rust-analyzer/crates/hir-def/src/db.rs
index b23427a73b3..270cfa06e58 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/db.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/db.rs
@@ -14,7 +14,7 @@ use crate::{
     body::{scope::ExprScopes, Body, BodySourceMap},
     data::{
         ConstData, FunctionData, ImplData, Macro2Data, MacroRulesData, ProcMacroData, StaticData,
-        TraitData, TypeAliasData,
+        TraitAliasData, TraitData, TypeAliasData,
     },
     generics::GenericParams,
     import_map::ImportMap,
@@ -25,8 +25,8 @@ use crate::{
     AttrDefId, BlockId, BlockLoc, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, ExternBlockId,
     ExternBlockLoc, FunctionId, FunctionLoc, GenericDefId, ImplId, ImplLoc, LocalEnumVariantId,
     LocalFieldId, Macro2Id, Macro2Loc, MacroRulesId, MacroRulesLoc, ProcMacroId, ProcMacroLoc,
-    StaticId, StaticLoc, StructId, StructLoc, TraitId, TraitLoc, TypeAliasId, TypeAliasLoc,
-    UnionId, UnionLoc, VariantId,
+    StaticId, StaticLoc, StructId, StructLoc, TraitAliasId, TraitAliasLoc, TraitId, TraitLoc,
+    TypeAliasId, TypeAliasLoc, UnionId, UnionLoc, VariantId,
 };
 
 #[salsa::query_group(InternDatabaseStorage)]
@@ -46,6 +46,8 @@ pub trait InternDatabase: SourceDatabase {
     #[salsa::interned]
     fn intern_trait(&self, loc: TraitLoc) -> TraitId;
     #[salsa::interned]
+    fn intern_trait_alias(&self, loc: TraitAliasLoc) -> TraitAliasId;
+    #[salsa::interned]
     fn intern_type_alias(&self, loc: TypeAliasLoc) -> TypeAliasId;
     #[salsa::interned]
     fn intern_impl(&self, loc: ImplLoc) -> ImplId;
@@ -125,6 +127,9 @@ pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> {
     #[salsa::invoke(TraitData::trait_data_with_diagnostics_query)]
     fn trait_data_with_diagnostics(&self, tr: TraitId) -> (Arc<TraitData>, Arc<[DefDiagnostic]>);
 
+    #[salsa::invoke(TraitAliasData::trait_alias_query)]
+    fn trait_alias_data(&self, e: TraitAliasId) -> Arc<TraitAliasData>;
+
     #[salsa::invoke(TypeAliasData::type_alias_data_query)]
     fn type_alias_data(&self, e: TypeAliasId) -> Arc<TypeAliasData>;
 
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr.rs
index 48028b7c6a8..bbea608c55e 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/expr.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/expr.rs
@@ -17,6 +17,7 @@ use std::fmt;
 use hir_expand::name::Name;
 use intern::Interned;
 use la_arena::{Idx, RawIdx};
+use smallvec::SmallVec;
 
 use crate::{
     builtin_type::{BuiltinFloat, BuiltinInt, BuiltinUint},
@@ -29,6 +30,8 @@ pub use syntax::ast::{ArithOp, BinaryOp, CmpOp, LogicOp, Ordering, RangeOp, Unar
 
 pub type ExprId = Idx<Expr>;
 
+pub type BindingId = Idx<Binding>;
+
 /// FIXME: this is a hacky function which should be removed
 pub(crate) fn dummy_expr_id() -> ExprId {
     ExprId::from_raw(RawIdx::from(u32::MAX))
@@ -52,13 +55,21 @@ pub type LabelId = Idx<Label>;
 // We convert float values into bits and that's how we don't need to deal with f32 and f64.
 // For PartialEq, bits comparison should work, as ordering is not important
 // https://github.com/rust-lang/rust-analyzer/issues/12380#issuecomment-1137284360
-#[derive(Default, Debug, Clone, Eq, PartialEq)]
+#[derive(Default, Debug, Clone, Copy, Eq, PartialEq)]
 pub struct FloatTypeWrapper(u64);
 
 impl FloatTypeWrapper {
     pub fn new(value: f64) -> Self {
         Self(value.to_bits())
     }
+
+    pub fn into_f64(self) -> f64 {
+        f64::from_bits(self.0)
+    }
+
+    pub fn into_f32(self) -> f32 {
+        f64::from_bits(self.0) as f32
+    }
 }
 
 impl fmt::Display for FloatTypeWrapper {
@@ -101,6 +112,26 @@ pub enum Expr {
         tail: Option<ExprId>,
         label: Option<LabelId>,
     },
+    TryBlock {
+        id: BlockId,
+        statements: Box<[Statement]>,
+        tail: Option<ExprId>,
+    },
+    Async {
+        id: BlockId,
+        statements: Box<[Statement]>,
+        tail: Option<ExprId>,
+    },
+    Const {
+        id: BlockId,
+        statements: Box<[Statement]>,
+        tail: Option<ExprId>,
+    },
+    Unsafe {
+        id: BlockId,
+        statements: Box<[Statement]>,
+        tail: Option<ExprId>,
+    },
     Loop {
         body: ExprId,
         label: Option<LabelId>,
@@ -164,15 +195,6 @@ pub enum Expr {
     Try {
         expr: ExprId,
     },
-    TryBlock {
-        body: ExprId,
-    },
-    Async {
-        body: ExprId,
-    },
-    Const {
-        body: ExprId,
-    },
     Cast {
         expr: ExprId,
         type_ref: Interned<TypeRef>,
@@ -214,9 +236,6 @@ pub enum Expr {
         exprs: Box<[ExprId]>,
         is_assignee_expr: bool,
     },
-    Unsafe {
-        body: ExprId,
-    },
     Array(Array),
     Literal(Literal),
     Underscore,
@@ -282,13 +301,20 @@ impl Expr {
             Expr::Let { expr, .. } => {
                 f(*expr);
             }
-            Expr::Block { statements, tail, .. } => {
+            Expr::Block { statements, tail, .. }
+            | Expr::TryBlock { statements, tail, .. }
+            | Expr::Unsafe { statements, tail, .. }
+            | Expr::Async { statements, tail, .. }
+            | Expr::Const { statements, tail, .. } => {
                 for stmt in statements.iter() {
                     match stmt {
-                        Statement::Let { initializer, .. } => {
+                        Statement::Let { initializer, else_branch, .. } => {
                             if let &Some(expr) = initializer {
                                 f(expr);
                             }
+                            if let &Some(expr) = else_branch {
+                                f(expr);
+                            }
                         }
                         Statement::Expr { expr: expression, .. } => f(*expression),
                     }
@@ -297,10 +323,6 @@ impl Expr {
                     f(expr);
                 }
             }
-            Expr::TryBlock { body }
-            | Expr::Unsafe { body }
-            | Expr::Async { body }
-            | Expr::Const { body } => f(*body),
             Expr::Loop { body, .. } => f(*body),
             Expr::While { condition, body, .. } => {
                 f(*condition);
@@ -415,6 +437,13 @@ impl BindingAnnotation {
 }
 
 #[derive(Debug, Clone, Eq, PartialEq)]
+pub struct Binding {
+    pub name: Name,
+    pub mode: BindingAnnotation,
+    pub definitions: SmallVec<[PatId; 1]>,
+}
+
+#[derive(Debug, Clone, Eq, PartialEq)]
 pub struct RecordFieldPat {
     pub name: Name,
     pub pat: PatId,
@@ -432,7 +461,7 @@ pub enum Pat {
     Slice { prefix: Box<[PatId]>, slice: Option<PatId>, suffix: Box<[PatId]> },
     Path(Box<Path>),
     Lit(ExprId),
-    Bind { mode: BindingAnnotation, name: Name, subpat: Option<PatId> },
+    Bind { id: BindingId, subpat: Option<PatId> },
     TupleStruct { path: Option<Box<Path>>, args: Box<[PatId]>, ellipsis: Option<usize> },
     Ref { pat: PatId, mutability: Mutability },
     Box { inner: PatId },
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/generics.rs b/src/tools/rust-analyzer/crates/hir-def/src/generics.rs
index b2ab0c30e03..e4912fa8a64 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/generics.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/generics.rs
@@ -187,6 +187,7 @@ impl GenericParams {
             GenericDefId::AdtId(AdtId::EnumId(id)) => id_to_generics!(id),
             GenericDefId::AdtId(AdtId::UnionId(id)) => id_to_generics!(id),
             GenericDefId::TraitId(id) => id_to_generics!(id),
+            GenericDefId::TraitAliasId(id) => id_to_generics!(id),
             GenericDefId::TypeAliasId(id) => id_to_generics!(id),
             GenericDefId::ImplId(id) => id_to_generics!(id),
             GenericDefId::EnumVariantId(_) | GenericDefId::ConstId(_) => {
@@ -207,12 +208,10 @@ impl GenericParams {
     pub(crate) fn fill_bounds(
         &mut self,
         lower_ctx: &LowerCtx<'_>,
-        node: &dyn ast::HasTypeBounds,
+        type_bounds: Option<ast::TypeBoundList>,
         target: Either<TypeRef, LifetimeRef>,
     ) {
-        for bound in
-            node.type_bound_list().iter().flat_map(|type_bound_list| type_bound_list.bounds())
-        {
+        for bound in type_bounds.iter().flat_map(|type_bound_list| type_bound_list.bounds()) {
             self.add_where_predicate_from_bound(lower_ctx, bound, None, target.clone());
         }
     }
@@ -233,7 +232,11 @@ impl GenericParams {
                     };
                     self.type_or_consts.alloc(param.into());
                     let type_ref = TypeRef::Path(name.into());
-                    self.fill_bounds(lower_ctx, &type_param, Either::Left(type_ref));
+                    self.fill_bounds(
+                        lower_ctx,
+                        type_param.type_bound_list(),
+                        Either::Left(type_ref),
+                    );
                 }
                 ast::TypeOrConstParam::Const(const_param) => {
                     let name = const_param.name().map_or_else(Name::missing, |it| it.as_name());
@@ -255,7 +258,11 @@ impl GenericParams {
             let param = LifetimeParamData { name: name.clone() };
             self.lifetimes.alloc(param);
             let lifetime_ref = LifetimeRef::new_name(name);
-            self.fill_bounds(lower_ctx, &lifetime_param, Either::Right(lifetime_ref));
+            self.fill_bounds(
+                lower_ctx,
+                lifetime_param.type_bound_list(),
+                Either::Right(lifetime_ref),
+            );
         }
     }
 
@@ -421,6 +428,10 @@ fn file_id_and_params_of(
             let src = it.lookup(db).source(db);
             (src.file_id, src.value.generic_param_list())
         }
+        GenericDefId::TraitAliasId(it) => {
+            let src = it.lookup(db).source(db);
+            (src.file_id, src.value.generic_param_list())
+        }
         GenericDefId::TypeAliasId(it) => {
             let src = it.lookup(db).source(db);
             (src.file_id, src.value.generic_param_list())
@@ -435,7 +446,7 @@ fn file_id_and_params_of(
 }
 
 impl HasChildSource<LocalTypeOrConstParamId> for GenericDefId {
-    type Value = Either<ast::TypeOrConstParam, ast::Trait>;
+    type Value = Either<ast::TypeOrConstParam, ast::TraitOrAlias>;
     fn child_source(
         &self,
         db: &dyn DefDatabase,
@@ -447,11 +458,20 @@ impl HasChildSource<LocalTypeOrConstParamId> for GenericDefId {
 
         let mut params = ArenaMap::default();
 
-        // For traits the first type index is `Self`, we need to add it before the other params.
-        if let GenericDefId::TraitId(id) = *self {
-            let trait_ref = id.lookup(db).source(db).value;
-            let idx = idx_iter.next().unwrap();
-            params.insert(idx, Either::Right(trait_ref));
+        // For traits and trait aliases the first type index is `Self`, we need to add it before
+        // the other params.
+        match *self {
+            GenericDefId::TraitId(id) => {
+                let trait_ref = id.lookup(db).source(db).value;
+                let idx = idx_iter.next().unwrap();
+                params.insert(idx, Either::Right(ast::TraitOrAlias::Trait(trait_ref)));
+            }
+            GenericDefId::TraitAliasId(id) => {
+                let alias = id.lookup(db).source(db).value;
+                let idx = idx_iter.next().unwrap();
+                params.insert(idx, Either::Right(ast::TraitOrAlias::TraitAlias(alias)));
+            }
+            _ => {}
         }
 
         if let Some(generic_params_list) = generic_params_list {
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs b/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs
index 1ce191942ec..4f1f6000db0 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs
@@ -264,6 +264,7 @@ pub enum ImportKind {
     Const,
     Static,
     Trait,
+    TraitAlias,
     TypeAlias,
     BuiltinType,
     AssociatedItem,
@@ -459,6 +460,7 @@ fn item_import_kind(item: ItemInNs) -> Option<ImportKind> {
         ModuleDefId::ConstId(_) => ImportKind::Const,
         ModuleDefId::StaticId(_) => ImportKind::Static,
         ModuleDefId::TraitId(_) => ImportKind::Trait,
+        ModuleDefId::TraitAliasId(_) => ImportKind::TraitAlias,
         ModuleDefId::TypeAliasId(_) => ImportKind::TypeAlias,
         ModuleDefId::BuiltinType(_) => ImportKind::BuiltinType,
         ModuleDefId::MacroId(_) => ImportKind::Macro,
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs
index 53a4173ff42..991e447033f 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs
@@ -431,6 +431,7 @@ impl PerNs {
             ModuleDefId::EnumVariantId(_) => PerNs::both(def, def, v),
             ModuleDefId::ConstId(_) | ModuleDefId::StaticId(_) => PerNs::values(def, v),
             ModuleDefId::TraitId(_) => PerNs::types(def, v),
+            ModuleDefId::TraitAliasId(_) => PerNs::types(def, v),
             ModuleDefId::TypeAliasId(_) => PerNs::types(def, v),
             ModuleDefId::BuiltinType(_) => PerNs::types(def, v),
             ModuleDefId::MacroId(mac) => PerNs::macros(mac, v),
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs
index 19d01630ef0..9da5b2d47c8 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs
@@ -204,6 +204,7 @@ impl ItemTree {
                 consts,
                 statics,
                 traits,
+                trait_aliases,
                 impls,
                 type_aliases,
                 mods,
@@ -226,6 +227,7 @@ impl ItemTree {
             consts.shrink_to_fit();
             statics.shrink_to_fit();
             traits.shrink_to_fit();
+            trait_aliases.shrink_to_fit();
             impls.shrink_to_fit();
             type_aliases.shrink_to_fit();
             mods.shrink_to_fit();
@@ -276,6 +278,7 @@ struct ItemTreeData {
     consts: Arena<Const>,
     statics: Arena<Static>,
     traits: Arena<Trait>,
+    trait_aliases: Arena<TraitAlias>,
     impls: Arena<Impl>,
     type_aliases: Arena<TypeAlias>,
     mods: Arena<Mod>,
@@ -496,6 +499,7 @@ mod_items! {
     Const in consts -> ast::Const,
     Static in statics -> ast::Static,
     Trait in traits -> ast::Trait,
+    TraitAlias in trait_aliases -> ast::TraitAlias,
     Impl in impls -> ast::Impl,
     TypeAlias in type_aliases -> ast::TypeAlias,
     Mod in mods -> ast::Module,
@@ -672,12 +676,19 @@ pub struct Trait {
     pub generic_params: Interned<GenericParams>,
     pub is_auto: bool,
     pub is_unsafe: bool,
-    /// This is [`None`] if this Trait is a trait alias.
-    pub items: Option<Box<[AssocItem]>>,
+    pub items: Box<[AssocItem]>,
     pub ast_id: FileAstId<ast::Trait>,
 }
 
 #[derive(Debug, Clone, Eq, PartialEq)]
+pub struct TraitAlias {
+    pub name: Name,
+    pub visibility: RawVisibilityId,
+    pub generic_params: Interned<GenericParams>,
+    pub ast_id: FileAstId<ast::TraitAlias>,
+}
+
+#[derive(Debug, Clone, Eq, PartialEq)]
 pub struct Impl {
     pub generic_params: Interned<GenericParams>,
     pub target_trait: Option<Interned<TraitRef>>,
@@ -872,6 +883,7 @@ impl ModItem {
             | ModItem::Enum(_)
             | ModItem::Static(_)
             | ModItem::Trait(_)
+            | ModItem::TraitAlias(_)
             | ModItem::Impl(_)
             | ModItem::Mod(_)
             | ModItem::MacroRules(_)
@@ -899,6 +911,7 @@ impl ModItem {
             ModItem::Const(it) => tree[it.index].ast_id().upcast(),
             ModItem::Static(it) => tree[it.index].ast_id().upcast(),
             ModItem::Trait(it) => tree[it.index].ast_id().upcast(),
+            ModItem::TraitAlias(it) => tree[it.index].ast_id().upcast(),
             ModItem::Impl(it) => tree[it.index].ast_id().upcast(),
             ModItem::TypeAlias(it) => tree[it.index].ast_id().upcast(),
             ModItem::Mod(it) => tree[it.index].ast_id().upcast(),
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs
index d4d3c5ef19a..77b186f8e3f 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs
@@ -3,7 +3,7 @@
 use std::{collections::hash_map::Entry, sync::Arc};
 
 use hir_expand::{ast_id_map::AstIdMap, hygiene::Hygiene, HirFileId};
-use syntax::ast::{self, HasModuleItem};
+use syntax::ast::{self, HasModuleItem, HasTypeBounds};
 
 use crate::{
     generics::{GenericParams, TypeParamData, TypeParamProvenance},
@@ -90,6 +90,13 @@ impl<'a> Ctx<'a> {
                 _ => None,
             })
             .collect();
+        if let Some(ast::Expr::MacroExpr(expr)) = block.tail_expr() {
+            if let Some(call) = expr.macro_call() {
+                if let Some(mod_item) = self.lower_mod_item(&call.into()) {
+                    self.tree.top_level.push(mod_item);
+                }
+            }
+        }
 
         self.tree
     }
@@ -110,6 +117,7 @@ impl<'a> Ctx<'a> {
             ast::Item::Const(ast) => self.lower_const(ast).into(),
             ast::Item::Module(ast) => self.lower_module(ast)?.into(),
             ast::Item::Trait(ast) => self.lower_trait(ast)?.into(),
+            ast::Item::TraitAlias(ast) => self.lower_trait_alias(ast)?.into(),
             ast::Item::Impl(ast) => self.lower_impl(ast)?.into(),
             ast::Item::Use(ast) => self.lower_use(ast)?.into(),
             ast::Item::ExternCrate(ast) => self.lower_extern_crate(ast)?.into(),
@@ -147,7 +155,7 @@ impl<'a> Ctx<'a> {
     fn lower_struct(&mut self, strukt: &ast::Struct) -> Option<FileItemTreeId<Struct>> {
         let visibility = self.lower_visibility(strukt);
         let name = strukt.name()?.as_name();
-        let generic_params = self.lower_generic_params(GenericsOwner::Struct, strukt);
+        let generic_params = self.lower_generic_params(HasImplicitSelf::No, strukt);
         let fields = self.lower_fields(&strukt.kind());
         let ast_id = self.source_ast_id_map.ast_id(strukt);
         let res = Struct { name, visibility, generic_params, fields, ast_id };
@@ -211,7 +219,7 @@ impl<'a> Ctx<'a> {
     fn lower_union(&mut self, union: &ast::Union) -> Option<FileItemTreeId<Union>> {
         let visibility = self.lower_visibility(union);
         let name = union.name()?.as_name();
-        let generic_params = self.lower_generic_params(GenericsOwner::Union, union);
+        let generic_params = self.lower_generic_params(HasImplicitSelf::No, union);
         let fields = match union.record_field_list() {
             Some(record_field_list) => self.lower_fields(&StructKind::Record(record_field_list)),
             None => Fields::Record(IdxRange::new(self.next_field_idx()..self.next_field_idx())),
@@ -224,7 +232,7 @@ impl<'a> Ctx<'a> {
     fn lower_enum(&mut self, enum_: &ast::Enum) -> Option<FileItemTreeId<Enum>> {
         let visibility = self.lower_visibility(enum_);
         let name = enum_.name()?.as_name();
-        let generic_params = self.lower_generic_params(GenericsOwner::Enum, enum_);
+        let generic_params = self.lower_generic_params(HasImplicitSelf::No, enum_);
         let variants = match &enum_.variant_list() {
             Some(variant_list) => self.lower_variants(variant_list),
             None => IdxRange::new(self.next_variant_idx()..self.next_variant_idx()),
@@ -372,8 +380,7 @@ impl<'a> Ctx<'a> {
             ast_id,
             flags,
         };
-        res.explicit_generic_params =
-            self.lower_generic_params(GenericsOwner::Function(&res), func);
+        res.explicit_generic_params = self.lower_generic_params(HasImplicitSelf::No, func);
 
         Some(id(self.data().functions.alloc(res)))
     }
@@ -386,7 +393,7 @@ impl<'a> Ctx<'a> {
         let type_ref = type_alias.ty().map(|it| self.lower_type_ref(&it));
         let visibility = self.lower_visibility(type_alias);
         let bounds = self.lower_type_bounds(type_alias);
-        let generic_params = self.lower_generic_params(GenericsOwner::TypeAlias, type_alias);
+        let generic_params = self.lower_generic_params(HasImplicitSelf::No, type_alias);
         let ast_id = self.source_ast_id_map.ast_id(type_alias);
         let res = TypeAlias {
             name,
@@ -442,27 +449,49 @@ impl<'a> Ctx<'a> {
     fn lower_trait(&mut self, trait_def: &ast::Trait) -> Option<FileItemTreeId<Trait>> {
         let name = trait_def.name()?.as_name();
         let visibility = self.lower_visibility(trait_def);
-        let generic_params = self.lower_generic_params(GenericsOwner::Trait(trait_def), trait_def);
+        let generic_params =
+            self.lower_generic_params(HasImplicitSelf::Yes(trait_def.type_bound_list()), trait_def);
         let is_auto = trait_def.auto_token().is_some();
         let is_unsafe = trait_def.unsafe_token().is_some();
-        let items = trait_def.assoc_item_list().map(|list| {
-            list.assoc_items()
-                .filter_map(|item| {
-                    let attrs = RawAttrs::new(self.db.upcast(), &item, self.hygiene());
-                    self.lower_assoc_item(&item).map(|item| {
-                        self.add_attrs(ModItem::from(item).into(), attrs);
-                        item
-                    })
-                })
-                .collect()
-        });
         let ast_id = self.source_ast_id_map.ast_id(trait_def);
-        let res = Trait { name, visibility, generic_params, is_auto, is_unsafe, items, ast_id };
-        Some(id(self.data().traits.alloc(res)))
+
+        let items = trait_def
+            .assoc_item_list()
+            .into_iter()
+            .flat_map(|list| list.assoc_items())
+            .filter_map(|item| {
+                let attrs = RawAttrs::new(self.db.upcast(), &item, self.hygiene());
+                self.lower_assoc_item(&item).map(|item| {
+                    self.add_attrs(ModItem::from(item).into(), attrs);
+                    item
+                })
+            })
+            .collect();
+
+        let def = Trait { name, visibility, generic_params, is_auto, is_unsafe, items, ast_id };
+        Some(id(self.data().traits.alloc(def)))
+    }
+
+    fn lower_trait_alias(
+        &mut self,
+        trait_alias_def: &ast::TraitAlias,
+    ) -> Option<FileItemTreeId<TraitAlias>> {
+        let name = trait_alias_def.name()?.as_name();
+        let visibility = self.lower_visibility(trait_alias_def);
+        let generic_params = self.lower_generic_params(
+            HasImplicitSelf::Yes(trait_alias_def.type_bound_list()),
+            trait_alias_def,
+        );
+        let ast_id = self.source_ast_id_map.ast_id(trait_alias_def);
+
+        let alias = TraitAlias { name, visibility, generic_params, ast_id };
+        Some(id(self.data().trait_aliases.alloc(alias)))
     }
 
     fn lower_impl(&mut self, impl_def: &ast::Impl) -> Option<FileItemTreeId<Impl>> {
-        let generic_params = self.lower_generic_params(GenericsOwner::Impl, impl_def);
+        // Note that trait impls don't get implicit `Self` unlike traits, because here they are a
+        // type alias rather than a type parameter, so this is handled by the resolver.
+        let generic_params = self.lower_generic_params(HasImplicitSelf::No, impl_def);
         // FIXME: If trait lowering fails, due to a non PathType for example, we treat this impl
         // as if it was an non-trait impl. Ideally we want to create a unique missing ref that only
         // equals itself.
@@ -566,42 +595,29 @@ impl<'a> Ctx<'a> {
 
     fn lower_generic_params(
         &mut self,
-        owner: GenericsOwner<'_>,
+        has_implicit_self: HasImplicitSelf,
         node: &dyn ast::HasGenericParams,
     ) -> Interned<GenericParams> {
         let mut generics = GenericParams::default();
-        match owner {
-            GenericsOwner::Function(_)
-            | GenericsOwner::Struct
-            | GenericsOwner::Enum
-            | GenericsOwner::Union
-            | GenericsOwner::TypeAlias => {
-                generics.fill(&self.body_ctx, node);
-            }
-            GenericsOwner::Trait(trait_def) => {
-                // traits get the Self type as an implicit first type parameter
-                generics.type_or_consts.alloc(
-                    TypeParamData {
-                        name: Some(name![Self]),
-                        default: None,
-                        provenance: TypeParamProvenance::TraitSelf,
-                    }
-                    .into(),
-                );
-                // add super traits as bounds on Self
-                // i.e., trait Foo: Bar is equivalent to trait Foo where Self: Bar
-                let self_param = TypeRef::Path(name![Self].into());
-                generics.fill_bounds(&self.body_ctx, trait_def, Either::Left(self_param));
-                generics.fill(&self.body_ctx, node);
-            }
-            GenericsOwner::Impl => {
-                // Note that we don't add `Self` here: in `impl`s, `Self` is not a
-                // type-parameter, but rather is a type-alias for impl's target
-                // type, so this is handled by the resolver.
-                generics.fill(&self.body_ctx, node);
-            }
+
+        if let HasImplicitSelf::Yes(bounds) = has_implicit_self {
+            // Traits and trait aliases get the Self type as an implicit first type parameter.
+            generics.type_or_consts.alloc(
+                TypeParamData {
+                    name: Some(name![Self]),
+                    default: None,
+                    provenance: TypeParamProvenance::TraitSelf,
+                }
+                .into(),
+            );
+            // add super traits as bounds on Self
+            // i.e., `trait Foo: Bar` is equivalent to `trait Foo where Self: Bar`
+            let self_param = TypeRef::Path(name![Self].into());
+            generics.fill_bounds(&self.body_ctx, bounds, Either::Left(self_param));
         }
 
+        generics.fill(&self.body_ctx, node);
+
         generics.shrink_to_fit();
         Interned::new(generics)
     }
@@ -673,17 +689,10 @@ fn desugar_future_path(orig: TypeRef) -> Path {
     Path::from_known_path(path, generic_args)
 }
 
-enum GenericsOwner<'a> {
-    /// We need access to the partially-lowered `Function` for lowering `impl Trait` in argument
-    /// position.
-    Function(&'a Function),
-    Struct,
-    Enum,
-    Union,
-    /// The `TraitDef` is needed to fill the source map for the implicit `Self` parameter.
-    Trait(&'a ast::Trait),
-    TypeAlias,
-    Impl,
+enum HasImplicitSelf {
+    /// Inner list is a type bound list for the implicit `Self`.
+    Yes(Option<ast::TypeBoundList>),
+    No,
 }
 
 fn lower_abi(abi: ast::Abi) -> Interned<str> {
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs
index 8f230b87d01..5f29997964b 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs
@@ -374,23 +374,24 @@ impl<'a> Printer<'a> {
                 }
                 w!(self, "trait {}", name);
                 self.print_generic_params(generic_params);
-                match items {
-                    Some(items) => {
-                        self.print_where_clause_and_opening_brace(generic_params);
-                        self.indented(|this| {
-                            for item in &**items {
-                                this.print_mod_item((*item).into());
-                            }
-                        });
-                    }
-                    None => {
-                        w!(self, " = ");
-                        // FIXME: Print the aliased traits
-                        self.print_where_clause_and_opening_brace(generic_params);
+                self.print_where_clause_and_opening_brace(generic_params);
+                self.indented(|this| {
+                    for item in &**items {
+                        this.print_mod_item((*item).into());
                     }
-                }
+                });
                 wln!(self, "}}");
             }
+            ModItem::TraitAlias(it) => {
+                let TraitAlias { name, visibility, generic_params, ast_id: _ } = &self.tree[it];
+                self.print_visibility(*visibility);
+                w!(self, "trait {}", name);
+                self.print_generic_params(generic_params);
+                w!(self, " = ");
+                self.print_where_clause(generic_params);
+                w!(self, ";");
+                wln!(self);
+            }
             ModItem::Impl(it) => {
                 let Impl { target_trait, self_ty, is_negative, items, generic_params, ast_id: _ } =
                     &self.tree[it];
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/keys.rs b/src/tools/rust-analyzer/crates/hir-def/src/keys.rs
index 72beec8186c..f30be6b64e3 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/keys.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/keys.rs
@@ -9,8 +9,8 @@ use syntax::{ast, AstNode, AstPtr};
 use crate::{
     dyn_map::{DynMap, Policy},
     ConstId, EnumId, EnumVariantId, FieldId, FunctionId, ImplId, LifetimeParamId, Macro2Id,
-    MacroRulesId, ProcMacroId, StaticId, StructId, TraitId, TypeAliasId, TypeOrConstParamId,
-    UnionId,
+    MacroRulesId, ProcMacroId, StaticId, StructId, TraitAliasId, TraitId, TypeAliasId,
+    TypeOrConstParamId, UnionId,
 };
 
 pub type Key<K, V> = crate::dyn_map::Key<K, V, AstPtrPolicy<K, V>>;
@@ -21,6 +21,7 @@ pub const STATIC: Key<ast::Static, StaticId> = Key::new();
 pub const TYPE_ALIAS: Key<ast::TypeAlias, TypeAliasId> = Key::new();
 pub const IMPL: Key<ast::Impl, ImplId> = Key::new();
 pub const TRAIT: Key<ast::Trait, TraitId> = Key::new();
+pub const TRAIT_ALIAS: Key<ast::TraitAlias, TraitAliasId> = Key::new();
 pub const STRUCT: Key<ast::Struct, StructId> = Key::new();
 pub const UNION: Key<ast::Union, UnionId> = Key::new();
 pub const ENUM: Key<ast::Enum, EnumId> = Key::new();
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs
index ab9bc615daf..4096e0a3826 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs
@@ -181,15 +181,15 @@ impl LangItems {
         T: Into<AttrDefId> + Copy,
     {
         let _p = profile::span("collect_lang_item");
-        if let Some(lang_item) = lang_attr(db, item).and_then(|it| LangItem::from_str(&it)) {
+        if let Some(lang_item) = lang_attr(db, item) {
             self.items.entry(lang_item).or_insert_with(|| constructor(item));
         }
     }
 }
 
-pub fn lang_attr(db: &dyn DefDatabase, item: impl Into<AttrDefId> + Copy) -> Option<SmolStr> {
+pub fn lang_attr(db: &dyn DefDatabase, item: impl Into<AttrDefId> + Copy) -> Option<LangItem> {
     let attrs = db.attrs(item.into());
-    attrs.by_key("lang").string_value().cloned()
+    attrs.by_key("lang").string_value().cloned().and_then(|it| LangItem::from_str(&it))
 }
 
 pub enum GenericRequirement {
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs
index d07c5fb67c6..8c2e93f0905 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs
@@ -86,7 +86,7 @@ use crate::{
     builtin_type::BuiltinType,
     item_tree::{
         Const, Enum, Function, Impl, ItemTreeId, ItemTreeNode, MacroDef, MacroRules, ModItem,
-        Static, Struct, Trait, TypeAlias, Union,
+        Static, Struct, Trait, TraitAlias, TypeAlias, Union,
     },
 };
 
@@ -128,7 +128,7 @@ impl ModuleId {
     }
 }
 
-/// An ID of a module, **local** to a specific crate
+/// An ID of a module, **local** to a `DefMap`.
 pub type LocalModuleId = Idx<nameres::ModuleData>;
 
 #[derive(Debug)]
@@ -262,6 +262,11 @@ pub type TraitLoc = ItemLoc<Trait>;
 impl_intern!(TraitId, TraitLoc, intern_trait, lookup_intern_trait);
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct TraitAliasId(salsa::InternId);
+pub type TraitAliasLoc = ItemLoc<TraitAlias>;
+impl_intern!(TraitAliasId, TraitAliasLoc, intern_trait_alias, lookup_intern_trait_alias);
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub struct TypeAliasId(salsa::InternId);
 type TypeAliasLoc = AssocItemLoc<TypeAlias>;
 impl_intern!(TypeAliasId, TypeAliasLoc, intern_type_alias, lookup_intern_type_alias);
@@ -453,6 +458,7 @@ pub enum ModuleDefId {
     ConstId(ConstId),
     StaticId(StaticId),
     TraitId(TraitId),
+    TraitAliasId(TraitAliasId),
     TypeAliasId(TypeAliasId),
     BuiltinType(BuiltinType),
     MacroId(MacroId),
@@ -466,6 +472,7 @@ impl_from!(
     ConstId,
     StaticId,
     TraitId,
+    TraitAliasId,
     TypeAliasId,
     BuiltinType
     for ModuleDefId
@@ -516,6 +523,7 @@ pub enum GenericDefId {
     FunctionId(FunctionId),
     AdtId(AdtId),
     TraitId(TraitId),
+    TraitAliasId(TraitAliasId),
     TypeAliasId(TypeAliasId),
     ImplId(ImplId),
     // enum variants cannot have generics themselves, but their parent enums
@@ -528,6 +536,7 @@ impl_from!(
     FunctionId,
     AdtId(StructId, EnumId, UnionId),
     TraitId,
+    TraitAliasId,
     TypeAliasId,
     ImplId,
     EnumVariantId,
@@ -555,6 +564,7 @@ pub enum AttrDefId {
     StaticId(StaticId),
     ConstId(ConstId),
     TraitId(TraitId),
+    TraitAliasId(TraitAliasId),
     TypeAliasId(TypeAliasId),
     MacroId(MacroId),
     ImplId(ImplId),
@@ -714,6 +724,7 @@ impl HasModule for GenericDefId {
             GenericDefId::FunctionId(it) => it.lookup(db).module(db),
             GenericDefId::AdtId(it) => it.module(db),
             GenericDefId::TraitId(it) => it.lookup(db).container,
+            GenericDefId::TraitAliasId(it) => it.lookup(db).container,
             GenericDefId::TypeAliasId(it) => it.lookup(db).module(db),
             GenericDefId::ImplId(it) => it.lookup(db).container,
             GenericDefId::EnumVariantId(it) => it.parent.lookup(db).container,
@@ -747,6 +758,7 @@ impl ModuleDefId {
             ModuleDefId::ConstId(id) => id.lookup(db).container.module(db),
             ModuleDefId::StaticId(id) => id.lookup(db).module(db),
             ModuleDefId::TraitId(id) => id.lookup(db).container,
+            ModuleDefId::TraitAliasId(id) => id.lookup(db).container,
             ModuleDefId::TypeAliasId(id) => id.lookup(db).module(db),
             ModuleDefId::MacroId(id) => id.module(db),
             ModuleDefId::BuiltinType(_) => return None,
@@ -765,6 +777,7 @@ impl AttrDefId {
             AttrDefId::StaticId(it) => it.lookup(db).module(db).krate,
             AttrDefId::ConstId(it) => it.lookup(db).module(db).krate,
             AttrDefId::TraitId(it) => it.lookup(db).container.krate,
+            AttrDefId::TraitAliasId(it) => it.lookup(db).container.krate,
             AttrDefId::TypeAliasId(it) => it.lookup(db).module(db).krate,
             AttrDefId::ImplId(it) => it.lookup(db).container.krate,
             AttrDefId::ExternBlockId(it) => it.lookup(db).container.krate,
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs
index bb45266725c..0b72ca1eec1 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs
@@ -143,7 +143,7 @@ macro_rules! assert {
 
 fn main() {
      {
-        if !true {
+        if !(true ) {
             $crate::panic!("{} {:?}", arg1(a, b, c), arg2);
         }
     };
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs
index 8358a46f0a9..b663a291789 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs
@@ -827,6 +827,7 @@ macro_rules! rgb_color {
 /* parse error: expected type */
 /* parse error: expected R_PAREN */
 /* parse error: expected R_ANGLE */
+/* parse error: expected `::` */
 /* parse error: expected COMMA */
 /* parse error: expected R_ANGLE */
 /* parse error: expected SEMICOLON */
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs
index f42b0079d76..7d7240e7e8c 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs
@@ -342,7 +342,7 @@ impl DefMap {
     }
 
     pub(crate) fn block_id(&self) -> Option<BlockId> {
-        self.block.as_ref().map(|block| block.block)
+        self.block.map(|block| block.block)
     }
 
     pub(crate) fn prelude(&self) -> Option<ModuleId> {
@@ -354,7 +354,7 @@ impl DefMap {
     }
 
     pub fn module_id(&self, local_id: LocalModuleId) -> ModuleId {
-        let block = self.block.as_ref().map(|b| b.block);
+        let block = self.block.map(|b| b.block);
         ModuleId { krate: self.krate, local_id, block }
     }
 
@@ -428,9 +428,9 @@ impl DefMap {
     /// Returns the module containing `local_mod`, either the parent `mod`, or the module containing
     /// the block, if `self` corresponds to a block expression.
     pub fn containing_module(&self, local_mod: LocalModuleId) -> Option<ModuleId> {
-        match &self[local_mod].parent {
-            Some(parent) => Some(self.module_id(*parent)),
-            None => self.block.as_ref().map(|block| block.parent),
+        match self[local_mod].parent {
+            Some(parent) => Some(self.module_id(parent)),
+            None => self.block.map(|block| block.parent),
         }
     }
 
@@ -440,11 +440,11 @@ impl DefMap {
         let mut buf = String::new();
         let mut arc;
         let mut current_map = self;
-        while let Some(block) = &current_map.block {
+        while let Some(block) = current_map.block {
             go(&mut buf, current_map, "block scope", current_map.root);
             buf.push('\n');
             arc = block.parent.def_map(db);
-            current_map = &*arc;
+            current_map = &arc;
         }
         go(&mut buf, current_map, "crate", current_map.root);
         return buf;
@@ -468,10 +468,10 @@ impl DefMap {
         let mut buf = String::new();
         let mut arc;
         let mut current_map = self;
-        while let Some(block) = &current_map.block {
+        while let Some(block) = current_map.block {
             format_to!(buf, "{:?} in {:?}\n", block.block, block.parent);
             arc = block.parent.def_map(db);
-            current_map = &*arc;
+            current_map = &arc;
         }
 
         format_to!(buf, "crate scope\n");
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs
index 4b39a20d86c..70acc3442c3 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs
@@ -51,7 +51,8 @@ use crate::{
     AdtId, AstId, AstIdWithPath, ConstLoc, EnumLoc, EnumVariantId, ExternBlockLoc, FunctionId,
     FunctionLoc, ImplLoc, Intern, ItemContainerId, LocalModuleId, Macro2Id, Macro2Loc,
     MacroExpander, MacroId, MacroRulesId, MacroRulesLoc, ModuleDefId, ModuleId, ProcMacroId,
-    ProcMacroLoc, StaticLoc, StructLoc, TraitLoc, TypeAliasLoc, UnionLoc, UnresolvedMacro,
+    ProcMacroLoc, StaticLoc, StructLoc, TraitAliasLoc, TraitLoc, TypeAliasLoc, UnionLoc,
+    UnresolvedMacro,
 };
 
 static GLOB_RECURSION_LIMIT: Limit = Limit::new(100);
@@ -666,8 +667,10 @@ impl DefCollector<'_> {
         macro_: Macro2Id,
         vis: &RawVisibility,
     ) {
-        let vis =
-            self.def_map.resolve_visibility(self.db, module_id, vis).unwrap_or(Visibility::Public);
+        let vis = self
+            .def_map
+            .resolve_visibility(self.db, module_id, vis, false)
+            .unwrap_or(Visibility::Public);
         self.def_map.modules[module_id].scope.declare(macro_.into());
         self.update(
             module_id,
@@ -831,7 +834,7 @@ impl DefCollector<'_> {
         let mut def = directive.status.namespaces();
         let vis = self
             .def_map
-            .resolve_visibility(self.db, module_id, &directive.import.visibility)
+            .resolve_visibility(self.db, module_id, &directive.import.visibility, false)
             .unwrap_or(Visibility::Public);
 
         match import.kind {
@@ -1547,7 +1550,7 @@ impl ModCollector<'_, '_> {
                 };
             let resolve_vis = |def_map: &DefMap, visibility| {
                 def_map
-                    .resolve_visibility(db, self.module_id, visibility)
+                    .resolve_visibility(db, self.module_id, visibility, false)
                     .unwrap_or(Visibility::Public)
             };
 
@@ -1707,6 +1710,20 @@ impl ModCollector<'_, '_> {
                         false,
                     );
                 }
+                ModItem::TraitAlias(id) => {
+                    let it = &self.item_tree[id];
+
+                    let vis = resolve_vis(def_map, &self.item_tree[it.visibility]);
+                    update_def(
+                        self.def_collector,
+                        TraitAliasLoc { container: module, id: ItemTreeId::new(self.tree_id, id) }
+                            .intern(db)
+                            .into(),
+                        &it.name,
+                        vis,
+                        false,
+                    );
+                }
                 ModItem::TypeAlias(id) => {
                     let it = &self.item_tree[id];
 
@@ -1823,7 +1840,7 @@ impl ModCollector<'_, '_> {
     ) -> LocalModuleId {
         let def_map = &mut self.def_collector.def_map;
         let vis = def_map
-            .resolve_visibility(self.def_collector.db, self.module_id, visibility)
+            .resolve_visibility(self.def_collector.db, self.module_id, visibility, false)
             .unwrap_or(Visibility::Public);
         let modules = &mut def_map.modules;
         let origin = match definition {
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs
index 1d9d5cccded..25478481dd0 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs
@@ -78,6 +78,7 @@ impl DefMap {
         // pub(path)
         //     ^^^^ this
         visibility: &RawVisibility,
+        within_impl: bool,
     ) -> Option<Visibility> {
         let mut vis = match visibility {
             RawVisibility::Module(path) => {
@@ -102,7 +103,8 @@ impl DefMap {
         // `super` to its parent (etc.). However, visibilities must only refer to a module in the
         // DefMap they're written in, so we restrict them when that happens.
         if let Visibility::Module(m) = vis {
-            if self.block_id() != m.block {
+            // ...unless we're resolving visibility for an associated item in an impl.
+            if self.block_id() != m.block && !within_impl {
                 cov_mark::hit!(adjust_vis_in_block_def_map);
                 vis = Visibility::Module(self.module_id(self.root()));
                 tracing::debug!("visibility {:?} points outside DefMap, adjusting to {:?}", m, vis);
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs
index f5190b76db0..13e6825f821 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs
@@ -223,6 +223,7 @@ pub type Ty = ();
                     ModuleDefId::ConstId(it) => drop(db.const_data(it)),
                     ModuleDefId::StaticId(it) => drop(db.static_data(it)),
                     ModuleDefId::TraitId(it) => drop(db.trait_data(it)),
+                    ModuleDefId::TraitAliasId(it) => drop(db.trait_alias_data(it)),
                     ModuleDefId::TypeAliasId(it) => drop(db.type_alias_data(it)),
                     ModuleDefId::EnumVariantId(_)
                     | ModuleDefId::ModuleId(_)
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/path.rs b/src/tools/rust-analyzer/crates/hir-def/src/path.rs
index 36d4c36a268..f3197d1800f 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/path.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/path.rs
@@ -8,7 +8,7 @@ use std::{
 
 use crate::{
     body::LowerCtx,
-    type_ref::{ConstScalarOrPath, LifetimeRef},
+    type_ref::{ConstRefOrPath, LifetimeRef},
 };
 use hir_expand::name::Name;
 use intern::Interned;
@@ -85,7 +85,7 @@ pub struct AssociatedTypeBinding {
 pub enum GenericArg {
     Type(TypeRef),
     Lifetime(LifetimeRef),
-    Const(ConstScalarOrPath),
+    Const(ConstRefOrPath),
 }
 
 impl Path {
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/path/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/path/lower.rs
index c85a11db6d1..b7542bd777d 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/path/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/path/lower.rs
@@ -2,7 +2,7 @@
 
 use std::iter;
 
-use crate::type_ref::ConstScalarOrPath;
+use crate::type_ref::ConstRefOrPath;
 
 use either::Either;
 use hir_expand::name::{name, AsName};
@@ -212,7 +212,7 @@ pub(super) fn lower_generic_args(
                 }
             }
             ast::GenericArg::ConstArg(arg) => {
-                let arg = ConstScalarOrPath::from_expr_opt(arg.expr());
+                let arg = ConstRefOrPath::from_expr_opt(arg.expr());
                 args.push(GenericArg::Const(arg))
             }
         }
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs
index 86958e3daea..61e64fc1036 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs
@@ -1,5 +1,5 @@
 //! Name resolution façade.
-use std::{hash::BuildHasherDefault, sync::Arc};
+use std::{fmt, hash::BuildHasherDefault, sync::Arc};
 
 use base_db::CrateId;
 use hir_expand::name::{name, Name};
@@ -12,7 +12,7 @@ use crate::{
     body::scope::{ExprScopes, ScopeId},
     builtin_type::BuiltinType,
     db::DefDatabase,
-    expr::{ExprId, LabelId, PatId},
+    expr::{BindingId, ExprId, LabelId},
     generics::{GenericParams, TypeOrConstParamData},
     item_scope::{BuiltinShadowMode, BUILTIN_SCOPE},
     nameres::DefMap,
@@ -22,7 +22,8 @@ use crate::{
     AdtId, AssocItemId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, ExternBlockId,
     FunctionId, GenericDefId, GenericParamId, HasModule, ImplId, ItemContainerId, LifetimeParamId,
     LocalModuleId, Lookup, Macro2Id, MacroId, MacroRulesId, ModuleDefId, ModuleId, ProcMacroId,
-    StaticId, StructId, TraitId, TypeAliasId, TypeOrConstParamId, TypeParamId, VariantId,
+    StaticId, StructId, TraitAliasId, TraitId, TypeAliasId, TypeOrConstParamId, TypeParamId,
+    VariantId,
 };
 
 #[derive(Debug, Clone)]
@@ -35,19 +36,34 @@ pub struct Resolver {
     module_scope: ModuleItemMap,
 }
 
-#[derive(Debug, Clone)]
+#[derive(Clone)]
 struct ModuleItemMap {
     def_map: Arc<DefMap>,
     module_id: LocalModuleId,
 }
 
-#[derive(Debug, Clone)]
+impl fmt::Debug for ModuleItemMap {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.debug_struct("ModuleItemMap").field("module_id", &self.module_id).finish()
+    }
+}
+
+#[derive(Clone)]
 struct ExprScope {
     owner: DefWithBodyId,
     expr_scopes: Arc<ExprScopes>,
     scope_id: ScopeId,
 }
 
+impl fmt::Debug for ExprScope {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.debug_struct("ExprScope")
+            .field("owner", &self.owner)
+            .field("scope_id", &self.scope_id)
+            .finish()
+    }
+}
+
 #[derive(Debug, Clone)]
 enum Scope {
     /// All the items and imported names of a module
@@ -74,6 +90,7 @@ pub enum TypeNs {
     TypeAliasId(TypeAliasId),
     BuiltinType(BuiltinType),
     TraitId(TraitId),
+    TraitAliasId(TraitAliasId),
     // Module belong to type ns, but the resolver is used when all module paths
     // are fully resolved.
     // ModuleId(ModuleId)
@@ -85,10 +102,10 @@ pub enum ResolveValueResult {
     Partial(TypeNs, usize),
 }
 
-#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
 pub enum ValueNs {
     ImplSelf(ImplId),
-    LocalBinding(PatId),
+    LocalBinding(BindingId),
     FunctionId(FunctionId),
     ConstId(ConstId),
     StaticId(StaticId),
@@ -214,10 +231,12 @@ impl Resolver {
         db: &dyn DefDatabase,
         visibility: &RawVisibility,
     ) -> Option<Visibility> {
+        let within_impl =
+            self.scopes().find(|scope| matches!(scope, Scope::ImplDefScope(_))).is_some();
         match visibility {
             RawVisibility::Module(_) => {
                 let (item_map, module) = self.item_scope();
-                item_map.resolve_visibility(db, module, visibility)
+                item_map.resolve_visibility(db, module, visibility, within_impl)
             }
             RawVisibility::Public => Some(Visibility::Public),
         }
@@ -236,69 +255,81 @@ impl Resolver {
             return self.module_scope.resolve_path_in_value_ns(db, path);
         }
 
-        for scope in self.scopes() {
-            match scope {
-                Scope::ExprScope(_) if n_segments > 1 => continue,
-                Scope::ExprScope(scope) => {
-                    let entry = scope
-                        .expr_scopes
-                        .entries(scope.scope_id)
-                        .iter()
-                        .find(|entry| entry.name() == first_name);
-
-                    if let Some(e) = entry {
-                        return Some(ResolveValueResult::ValueNs(ValueNs::LocalBinding(e.pat())));
+        if n_segments <= 1 {
+            for scope in self.scopes() {
+                match scope {
+                    Scope::ExprScope(scope) => {
+                        let entry = scope
+                            .expr_scopes
+                            .entries(scope.scope_id)
+                            .iter()
+                            .find(|entry| entry.name() == first_name);
+
+                        if let Some(e) = entry {
+                            return Some(ResolveValueResult::ValueNs(ValueNs::LocalBinding(
+                                e.binding(),
+                            )));
+                        }
                     }
-                }
-                Scope::GenericParams { params, def } if n_segments > 1 => {
-                    if let Some(id) = params.find_type_by_name(first_name, *def) {
-                        let ty = TypeNs::GenericParam(id);
-                        return Some(ResolveValueResult::Partial(ty, 1));
+                    Scope::GenericParams { params, def } => {
+                        if let Some(id) = params.find_const_by_name(first_name, *def) {
+                            let val = ValueNs::GenericParam(id);
+                            return Some(ResolveValueResult::ValueNs(val));
+                        }
                     }
-                }
-                Scope::GenericParams { .. } if n_segments != 1 => continue,
-                Scope::GenericParams { params, def } => {
-                    if let Some(id) = params.find_const_by_name(first_name, *def) {
-                        let val = ValueNs::GenericParam(id);
-                        return Some(ResolveValueResult::ValueNs(val));
+                    &Scope::ImplDefScope(impl_) => {
+                        if first_name == &name![Self] {
+                            return Some(ResolveValueResult::ValueNs(ValueNs::ImplSelf(impl_)));
+                        }
                     }
-                }
-
-                &Scope::ImplDefScope(impl_) => {
-                    if first_name == &name![Self] {
-                        return Some(if n_segments > 1 {
-                            ResolveValueResult::Partial(TypeNs::SelfType(impl_), 1)
-                        } else {
-                            ResolveValueResult::ValueNs(ValueNs::ImplSelf(impl_))
-                        });
+                    // bare `Self` doesn't work in the value namespace in a struct/enum definition
+                    Scope::AdtScope(_) => continue,
+                    Scope::BlockScope(m) => {
+                        if let Some(def) = m.resolve_path_in_value_ns(db, path) {
+                            return Some(def);
+                        }
                     }
                 }
-                // bare `Self` doesn't work in the value namespace in a struct/enum definition
-                Scope::AdtScope(_) if n_segments == 1 => continue,
-                Scope::AdtScope(adt) => {
-                    if first_name == &name![Self] {
-                        let ty = TypeNs::AdtSelfType(*adt);
-                        return Some(ResolveValueResult::Partial(ty, 1));
+            }
+        } else {
+            for scope in self.scopes() {
+                match scope {
+                    Scope::ExprScope(_) => continue,
+                    Scope::GenericParams { params, def } => {
+                        if let Some(id) = params.find_type_by_name(first_name, *def) {
+                            let ty = TypeNs::GenericParam(id);
+                            return Some(ResolveValueResult::Partial(ty, 1));
+                        }
                     }
-                }
-
-                Scope::BlockScope(m) => {
-                    if let Some(def) = m.resolve_path_in_value_ns(db, path) {
-                        return Some(def);
+                    &Scope::ImplDefScope(impl_) => {
+                        if first_name == &name![Self] {
+                            return Some(ResolveValueResult::Partial(TypeNs::SelfType(impl_), 1));
+                        }
+                    }
+                    Scope::AdtScope(adt) => {
+                        if first_name == &name![Self] {
+                            let ty = TypeNs::AdtSelfType(*adt);
+                            return Some(ResolveValueResult::Partial(ty, 1));
+                        }
+                    }
+                    Scope::BlockScope(m) => {
+                        if let Some(def) = m.resolve_path_in_value_ns(db, path) {
+                            return Some(def);
+                        }
                     }
                 }
             }
         }
 
-        if let res @ Some(_) = self.module_scope.resolve_path_in_value_ns(db, path) {
-            return res;
+        if let Some(res) = self.module_scope.resolve_path_in_value_ns(db, path) {
+            return Some(res);
         }
 
         // If a path of the shape `u16::from_le_bytes` failed to resolve at all, then we fall back
         // to resolving to the primitive type, to allow this to still work in the presence of
         // `use core::u16;`.
-        if path.kind == PathKind::Plain && path.segments().len() > 1 {
-            if let Some(builtin) = BuiltinType::by_name(&path.segments()[0]) {
+        if path.kind == PathKind::Plain && n_segments > 1 {
+            if let Some(builtin) = BuiltinType::by_name(first_name) {
                 return Some(ResolveValueResult::Partial(TypeNs::BuiltinType(builtin), 1));
             }
         }
@@ -400,6 +431,8 @@ impl Resolver {
     }
 
     pub fn traits_in_scope(&self, db: &dyn DefDatabase) -> FxHashSet<TraitId> {
+        // FIXME(trait_alias): Trait alias brings aliased traits in scope! Note that supertraits of
+        // aliased traits are NOT brought in scope (unless also aliased).
         let mut traits = FxHashSet::default();
 
         for scope in self.scopes() {
@@ -428,6 +461,15 @@ impl Resolver {
         traits
     }
 
+    pub fn traits_in_scope_from_block_scopes(&self) -> impl Iterator<Item = TraitId> + '_ {
+        self.scopes()
+            .filter_map(|scope| match scope {
+                Scope::BlockScope(m) => Some(m.def_map[m.module_id].scope.traits()),
+                _ => None,
+            })
+            .flatten()
+    }
+
     pub fn module(&self) -> ModuleId {
         let (def_map, local_id) = self.item_scope();
         def_map.module_id(local_id)
@@ -459,14 +501,85 @@ impl Resolver {
         })
     }
 
+    pub fn generic_params(&self) -> Option<&Interned<GenericParams>> {
+        self.scopes().find_map(|scope| match scope {
+            Scope::GenericParams { params, .. } => Some(params),
+            _ => None,
+        })
+    }
+
     pub fn body_owner(&self) -> Option<DefWithBodyId> {
         self.scopes().find_map(|scope| match scope {
             Scope::ExprScope(it) => Some(it.owner),
             _ => None,
         })
     }
+    /// `expr_id` is required to be an expression id that comes after the top level expression scope in the given resolver
+    #[must_use]
+    pub fn update_to_inner_scope(
+        &mut self,
+        db: &dyn DefDatabase,
+        owner: DefWithBodyId,
+        expr_id: ExprId,
+    ) -> UpdateGuard {
+        #[inline(always)]
+        fn append_expr_scope(
+            db: &dyn DefDatabase,
+            resolver: &mut Resolver,
+            owner: DefWithBodyId,
+            expr_scopes: &Arc<ExprScopes>,
+            scope_id: ScopeId,
+        ) {
+            resolver.scopes.push(Scope::ExprScope(ExprScope {
+                owner,
+                expr_scopes: expr_scopes.clone(),
+                scope_id,
+            }));
+            if let Some(block) = expr_scopes.block(scope_id) {
+                if let Some(def_map) = db.block_def_map(block) {
+                    let root = def_map.root();
+                    resolver
+                        .scopes
+                        .push(Scope::BlockScope(ModuleItemMap { def_map, module_id: root }));
+                    // FIXME: This adds as many module scopes as there are blocks, but resolving in each
+                    // already traverses all parents, so this is O(n²). I think we could only store the
+                    // innermost module scope instead?
+                }
+            }
+        }
+
+        let start = self.scopes.len();
+        let innermost_scope = self.scopes().next();
+        match innermost_scope {
+            Some(&Scope::ExprScope(ExprScope { scope_id, ref expr_scopes, owner })) => {
+                let expr_scopes = expr_scopes.clone();
+                let scope_chain = expr_scopes
+                    .scope_chain(expr_scopes.scope_for(expr_id))
+                    .take_while(|&it| it != scope_id);
+                for scope_id in scope_chain {
+                    append_expr_scope(db, self, owner, &expr_scopes, scope_id);
+                }
+            }
+            _ => {
+                let expr_scopes = db.expr_scopes(owner);
+                let scope_chain = expr_scopes.scope_chain(expr_scopes.scope_for(expr_id));
+
+                for scope_id in scope_chain {
+                    append_expr_scope(db, self, owner, &expr_scopes, scope_id);
+                }
+            }
+        }
+        self.scopes[start..].reverse();
+        UpdateGuard(start)
+    }
+
+    pub fn reset_to_guard(&mut self, UpdateGuard(start): UpdateGuard) {
+        self.scopes.truncate(start);
+    }
 }
 
+pub struct UpdateGuard(usize);
+
 impl Resolver {
     fn scopes(&self) -> impl Iterator<Item = &Scope> {
         self.scopes.iter().rev()
@@ -504,7 +617,7 @@ pub enum ScopeDef {
     ImplSelfType(ImplId),
     AdtSelfType(AdtId),
     GenericParam(GenericParamId),
-    Local(PatId),
+    Local(BindingId),
     Label(LabelId),
 }
 
@@ -556,17 +669,18 @@ impl Scope {
                     acc.add(&name, ScopeDef::Label(label))
                 }
                 scope.expr_scopes.entries(scope.scope_id).iter().for_each(|e| {
-                    acc.add_local(e.name(), e.pat());
+                    acc.add_local(e.name(), e.binding());
                 });
             }
         }
     }
 }
 
-// needs arbitrary_self_types to be a method... or maybe move to the def?
 pub fn resolver_for_expr(db: &dyn DefDatabase, owner: DefWithBodyId, expr_id: ExprId) -> Resolver {
+    let r = owner.resolver(db);
     let scopes = db.expr_scopes(owner);
-    resolver_for_scope(db, owner, scopes.scope_for(expr_id))
+    let scope_id = scopes.scope_for(expr_id);
+    resolver_for_scope_(db, scopes, scope_id, r, owner)
 }
 
 pub fn resolver_for_scope(
@@ -574,8 +688,18 @@ pub fn resolver_for_scope(
     owner: DefWithBodyId,
     scope_id: Option<ScopeId>,
 ) -> Resolver {
-    let mut r = owner.resolver(db);
+    let r = owner.resolver(db);
     let scopes = db.expr_scopes(owner);
+    resolver_for_scope_(db, scopes, scope_id, r, owner)
+}
+
+fn resolver_for_scope_(
+    db: &dyn DefDatabase,
+    scopes: Arc<ExprScopes>,
+    scope_id: Option<ScopeId>,
+    mut r: Resolver,
+    owner: DefWithBodyId,
+) -> Resolver {
     let scope_chain = scopes.scope_chain(scope_id).collect::<Vec<_>>();
     r.scopes.reserve(scope_chain.len());
 
@@ -641,6 +765,7 @@ impl ModuleItemMap {
                 let ty = match module_def.take_types()? {
                     ModuleDefId::AdtId(it) => TypeNs::AdtId(it),
                     ModuleDefId::TraitId(it) => TypeNs::TraitId(it),
+                    ModuleDefId::TraitAliasId(it) => TypeNs::TraitAliasId(it),
                     ModuleDefId::TypeAliasId(it) => TypeNs::TypeAliasId(it),
                     ModuleDefId::BuiltinType(it) => TypeNs::BuiltinType(it),
 
@@ -678,6 +803,7 @@ fn to_value_ns(per_ns: PerNs) -> Option<ValueNs> {
 
         ModuleDefId::AdtId(AdtId::EnumId(_) | AdtId::UnionId(_))
         | ModuleDefId::TraitId(_)
+        | ModuleDefId::TraitAliasId(_)
         | ModuleDefId::TypeAliasId(_)
         | ModuleDefId::BuiltinType(_)
         | ModuleDefId::MacroId(_)
@@ -695,6 +821,7 @@ fn to_type_ns(per_ns: PerNs) -> Option<TypeNs> {
         ModuleDefId::BuiltinType(it) => TypeNs::BuiltinType(it),
 
         ModuleDefId::TraitId(it) => TypeNs::TraitId(it),
+        ModuleDefId::TraitAliasId(it) => TypeNs::TraitAliasId(it),
 
         ModuleDefId::FunctionId(_)
         | ModuleDefId::ConstId(_)
@@ -732,7 +859,7 @@ impl ScopeNames {
             self.add(name, ScopeDef::Unknown)
         }
     }
-    fn add_local(&mut self, name: &Name, pat: PatId) {
+    fn add_local(&mut self, name: &Name, binding: BindingId) {
         let set = self.map.entry(name.clone()).or_default();
         // XXX: hack, account for local (and only local) shadowing.
         //
@@ -743,7 +870,7 @@ impl ScopeNames {
             cov_mark::hit!(shadowing_shows_single_completion);
             return;
         }
-        set.push(ScopeDef::Local(pat))
+        set.push(ScopeDef::Local(binding))
     }
 }
 
@@ -779,6 +906,12 @@ impl HasResolver for TraitId {
     }
 }
 
+impl HasResolver for TraitAliasId {
+    fn resolver(self, db: &dyn DefDatabase) -> Resolver {
+        self.lookup(db).container.resolver(db).push_generic_params_scope(db, self.into())
+    }
+}
+
 impl<T: Into<AdtId> + Copy> HasResolver for T {
     fn resolver(self, db: &dyn DefDatabase) -> Resolver {
         let def = self.into();
@@ -858,6 +991,7 @@ impl HasResolver for GenericDefId {
             GenericDefId::FunctionId(inner) => inner.resolver(db),
             GenericDefId::AdtId(adt) => adt.resolver(db),
             GenericDefId::TraitId(inner) => inner.resolver(db),
+            GenericDefId::TraitAliasId(inner) => inner.resolver(db),
             GenericDefId::TypeAliasId(inner) => inner.resolver(db),
             GenericDefId::ImplId(inner) => inner.resolver(db),
             GenericDefId::EnumVariantId(inner) => inner.parent.resolver(db),
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/type_ref.rs b/src/tools/rust-analyzer/crates/hir-def/src/type_ref.rs
index 9652b01b91b..8e30f429a9f 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/type_ref.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/type_ref.rs
@@ -116,7 +116,7 @@ pub enum TypeRef {
     Reference(Box<TypeRef>, Option<LifetimeRef>, Mutability),
     // FIXME: for full const generics, the latter element (length) here is going to have to be an
     // expression that is further lowered later in hir_ty.
-    Array(Box<TypeRef>, ConstScalarOrPath),
+    Array(Box<TypeRef>, ConstRefOrPath),
     Slice(Box<TypeRef>),
     /// A fn pointer. Last element of the vector is the return type.
     Fn(Vec<(Option<Name>, TypeRef)>, bool /*varargs*/, bool /*is_unsafe*/),
@@ -188,7 +188,7 @@ impl TypeRef {
                 // `hir_def::body::lower` to lower this into an `Expr` and then evaluate it at the
                 // `hir_ty` level, which would allow knowing the type of:
                 // let v: [u8; 2 + 2] = [0u8; 4];
-                let len = ConstScalarOrPath::from_expr_opt(inner.expr());
+                let len = ConstRefOrPath::from_expr_opt(inner.expr());
                 TypeRef::Array(Box::new(TypeRef::from_ast_opt(ctx, inner.ty())), len)
             }
             ast::Type::SliceType(inner) => {
@@ -378,25 +378,25 @@ impl TypeBound {
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
-pub enum ConstScalarOrPath {
-    Scalar(ConstScalar),
+pub enum ConstRefOrPath {
+    Scalar(ConstRef),
     Path(Name),
 }
 
-impl std::fmt::Display for ConstScalarOrPath {
+impl std::fmt::Display for ConstRefOrPath {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         match self {
-            ConstScalarOrPath::Scalar(s) => s.fmt(f),
-            ConstScalarOrPath::Path(n) => n.fmt(f),
+            ConstRefOrPath::Scalar(s) => s.fmt(f),
+            ConstRefOrPath::Path(n) => n.fmt(f),
         }
     }
 }
 
-impl ConstScalarOrPath {
+impl ConstRefOrPath {
     pub(crate) fn from_expr_opt(expr: Option<ast::Expr>) -> Self {
         match expr {
             Some(x) => Self::from_expr(x),
-            None => Self::Scalar(ConstScalar::Unknown),
+            None => Self::Scalar(ConstRef::Unknown),
         }
     }
 
@@ -407,7 +407,7 @@ impl ConstScalarOrPath {
             ast::Expr::PathExpr(p) => {
                 match p.path().and_then(|x| x.segment()).and_then(|x| x.name_ref()) {
                     Some(x) => Self::Path(x.as_name()),
-                    None => Self::Scalar(ConstScalar::Unknown),
+                    None => Self::Scalar(ConstRef::Unknown),
                 }
             }
             ast::Expr::PrefixExpr(prefix_expr) => match prefix_expr.op_kind() {
@@ -415,8 +415,8 @@ impl ConstScalarOrPath {
                     let unsigned = Self::from_expr_opt(prefix_expr.expr());
                     // Add sign
                     match unsigned {
-                        Self::Scalar(ConstScalar::UInt(num)) => {
-                            Self::Scalar(ConstScalar::Int(-(num as i128)))
+                        Self::Scalar(ConstRef::UInt(num)) => {
+                            Self::Scalar(ConstRef::Int(-(num as i128)))
                         }
                         other => other,
                     }
@@ -425,22 +425,22 @@ impl ConstScalarOrPath {
             },
             ast::Expr::Literal(literal) => Self::Scalar(match literal.kind() {
                 ast::LiteralKind::IntNumber(num) => {
-                    num.value().map(ConstScalar::UInt).unwrap_or(ConstScalar::Unknown)
+                    num.value().map(ConstRef::UInt).unwrap_or(ConstRef::Unknown)
                 }
                 ast::LiteralKind::Char(c) => {
-                    c.value().map(ConstScalar::Char).unwrap_or(ConstScalar::Unknown)
+                    c.value().map(ConstRef::Char).unwrap_or(ConstRef::Unknown)
                 }
-                ast::LiteralKind::Bool(f) => ConstScalar::Bool(f),
-                _ => ConstScalar::Unknown,
+                ast::LiteralKind::Bool(f) => ConstRef::Bool(f),
+                _ => ConstRef::Unknown,
             }),
-            _ => Self::Scalar(ConstScalar::Unknown),
+            _ => Self::Scalar(ConstRef::Unknown),
         }
     }
 }
 
 /// A concrete constant value
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub enum ConstScalar {
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub enum ConstRef {
     Int(i128),
     UInt(u128),
     Bool(bool),
@@ -454,18 +454,18 @@ pub enum ConstScalar {
     Unknown,
 }
 
-impl ConstScalar {
+impl ConstRef {
     pub fn builtin_type(&self) -> BuiltinType {
         match self {
-            ConstScalar::UInt(_) | ConstScalar::Unknown => BuiltinType::Uint(BuiltinUint::U128),
-            ConstScalar::Int(_) => BuiltinType::Int(BuiltinInt::I128),
-            ConstScalar::Char(_) => BuiltinType::Char,
-            ConstScalar::Bool(_) => BuiltinType::Bool,
+            ConstRef::UInt(_) | ConstRef::Unknown => BuiltinType::Uint(BuiltinUint::U128),
+            ConstRef::Int(_) => BuiltinType::Int(BuiltinInt::I128),
+            ConstRef::Char(_) => BuiltinType::Char,
+            ConstRef::Bool(_) => BuiltinType::Bool,
         }
     }
 }
 
-impl From<Literal> for ConstScalar {
+impl From<Literal> for ConstRef {
     fn from(literal: Literal) -> Self {
         match literal {
             Literal::Char(c) => Self::Char(c),
@@ -477,14 +477,14 @@ impl From<Literal> for ConstScalar {
     }
 }
 
-impl std::fmt::Display for ConstScalar {
+impl std::fmt::Display for ConstRef {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
         match self {
-            ConstScalar::Int(num) => num.fmt(f),
-            ConstScalar::UInt(num) => num.fmt(f),
-            ConstScalar::Bool(flag) => flag.fmt(f),
-            ConstScalar::Char(c) => write!(f, "'{c}'"),
-            ConstScalar::Unknown => f.write_char('_'),
+            ConstRef::Int(num) => num.fmt(f),
+            ConstRef::UInt(num) => num.fmt(f),
+            ConstRef::Bool(flag) => flag.fmt(f),
+            ConstRef::Char(c) => write!(f, "'{c}'"),
+            ConstRef::Unknown => f.write_char('_'),
         }
     }
 }
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/visibility.rs b/src/tools/rust-analyzer/crates/hir-def/src/visibility.rs
index 087268a9ece..c9fcaae56cf 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/visibility.rs
+++ b/src/tools/rust-analyzer/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,
+    ConstId, FunctionId, HasModule, LocalFieldId, LocalModuleId, ModuleId, VariantId,
 };
 
 /// Visibility of an item, not yet resolved.
@@ -120,7 +120,7 @@ impl Visibility {
         self,
         db: &dyn DefDatabase,
         def_map: &DefMap,
-        mut from_module: crate::LocalModuleId,
+        mut from_module: LocalModuleId,
     ) -> bool {
         let mut to_module = match self {
             Visibility::Module(m) => m,
@@ -142,7 +142,8 @@ impl Visibility {
                 arc = to_module.def_map(db);
                 &arc
             };
-        let is_block_root = matches!(to_module.block, Some(_) if to_module_def_map[to_module.local_id].parent.is_none());
+        let is_block_root =
+            to_module.block.is_some() && to_module_def_map[to_module.local_id].parent.is_none();
         if is_block_root {
             to_module = to_module_def_map.containing_module(to_module.local_id).unwrap();
         }
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs
index 9f3fa73d4e6..295083a37f2 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs
@@ -206,7 +206,7 @@ fn assert_expand(
             let cond = cond.clone();
             let panic_args = itertools::Itertools::intersperse(panic_args.iter().cloned(), comma);
             quote! {{
-                if !#cond {
+                if !(#cond) {
                     #DOLLAR_CRATE::panic!(##panic_args);
                 }
             }}
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs
index a52716cc02c..39fc08ecdcf 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs
@@ -771,6 +771,15 @@ impl<T> InFile<Option<T>> {
     }
 }
 
+impl<L, R> InFile<Either<L, R>> {
+    pub fn transpose(self) -> Either<InFile<L>, InFile<R>> {
+        match self.value {
+            Either::Left(l) => Either::Left(InFile::new(self.file_id, l)),
+            Either::Right(r) => Either::Right(InFile::new(self.file_id, r)),
+        }
+    }
+}
+
 impl<'a> InFile<&'a SyntaxNode> {
     pub fn ancestors_with_macros(
         self,
@@ -806,7 +815,7 @@ impl<'a> InFile<&'a SyntaxNode> {
     /// Falls back to the macro call range if the node cannot be mapped up fully.
     ///
     /// For attributes and derives, this will point back to the attribute only.
-    /// For the entire item `InFile::use original_file_range_full`.
+    /// For the entire item use [`InFile::original_file_range_full`].
     pub fn original_file_range(self, db: &dyn db::AstDatabase) -> FileRange {
         match self.file_id.repr() {
             HirFileIdRepr::FileId(file_id) => FileRange { file_id, range: self.value.text_range() },
@@ -821,6 +830,21 @@ impl<'a> InFile<&'a SyntaxNode> {
         }
     }
 
+    /// Falls back to the macro call range if the node cannot be mapped up fully.
+    pub fn original_file_range_full(self, db: &dyn db::AstDatabase) -> FileRange {
+        match self.file_id.repr() {
+            HirFileIdRepr::FileId(file_id) => FileRange { file_id, range: self.value.text_range() },
+            HirFileIdRepr::MacroFile(mac_file) => {
+                if let Some(res) = self.original_file_range_opt(db) {
+                    return res;
+                }
+                // Fall back to whole macro call.
+                let loc = db.lookup_intern_macro_call(mac_file.macro_call_id);
+                loc.kind.original_call_range_with_body(db)
+            }
+        }
+    }
+
     /// Attempts to map the syntax node back up its macro calls.
     pub fn original_file_range_opt(self, db: &dyn db::AstDatabase) -> Option<FileRange> {
         match ascend_node_border_tokens(db, self) {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml
index a8b8d5222e4..4572e33486f 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml
@@ -18,6 +18,7 @@ arrayvec = "0.7.2"
 bitflags = "1.3.2"
 smallvec.workspace = true
 ena = "0.14.0"
+either = "1.7.0"
 tracing = "0.1.35"
 rustc-hash = "1.1.0"
 scoped-tls = "1.0.0"
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs b/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs
index 8faef7bf71e..03e9443599d 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs
@@ -152,6 +152,15 @@ impl TyBuilder<()> {
         TyKind::Tuple(0, Substitution::empty(Interner)).intern(Interner)
     }
 
+    // FIXME: rustc's ty is dependent on the adt type, maybe we need to do that as well
+    pub fn discr_ty() -> Ty {
+        TyKind::Scalar(chalk_ir::Scalar::Int(chalk_ir::IntTy::I128)).intern(Interner)
+    }
+
+    pub fn bool() -> Ty {
+        TyKind::Scalar(chalk_ir::Scalar::Bool).intern(Interner)
+    }
+
     pub fn usize() -> Ty {
         TyKind::Scalar(chalk_ir::Scalar::Uint(chalk_ir::UintTy::Usize)).intern(Interner)
     }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs
index 6989e9fb9be..28ae4c349f8 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs
@@ -540,8 +540,7 @@ pub(crate) fn trait_datum_query(
     let where_clauses = convert_where_clauses(db, trait_.into(), &bound_vars);
     let associated_ty_ids = trait_data.associated_types().map(to_assoc_type_id).collect();
     let trait_datum_bound = rust_ir::TraitDatumBound { where_clauses };
-    let well_known = lang_attr(db.upcast(), trait_)
-        .and_then(|name| well_known_trait_from_lang_item(LangItem::from_str(&name)?));
+    let well_known = lang_attr(db.upcast(), trait_).and_then(well_known_trait_from_lang_item);
     let trait_datum = TraitDatum {
         id: trait_id,
         binders: make_binders(db, &generic_params, trait_datum_bound),
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs
index 45c975dfcdc..e6aefbf2716 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs
@@ -13,7 +13,7 @@ use crate::{
     db::HirDatabase, from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id,
     from_placeholder_idx, to_chalk_trait_id, utils::generics, AdtId, AliasEq, AliasTy, Binders,
     CallableDefId, CallableSig, FnPointer, ImplTraitId, Interner, Lifetime, ProjectionTy,
-    QuantifiedWhereClause, Substitution, TraitRef, Ty, TyBuilder, TyKind, WhereClause,
+    QuantifiedWhereClause, Substitution, TraitRef, Ty, TyBuilder, TyKind, TypeFlags, WhereClause,
 };
 
 pub trait TyExt {
@@ -22,6 +22,7 @@ pub trait TyExt {
     fn is_floating_point(&self) -> bool;
     fn is_never(&self) -> bool;
     fn is_unknown(&self) -> bool;
+    fn contains_unknown(&self) -> bool;
     fn is_ty_var(&self) -> bool;
 
     fn as_adt(&self) -> Option<(hir_def::AdtId, &Substitution)>;
@@ -76,6 +77,10 @@ impl TyExt for Ty {
         matches!(self.kind(Interner), TyKind::Error)
     }
 
+    fn contains_unknown(&self) -> bool {
+        self.data(Interner).flags.contains(TypeFlags::HAS_ERROR)
+    }
+
     fn is_ty_var(&self) -> bool {
         matches!(self.kind(Interner), TyKind::InferenceVar(_, _))
     }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs
index 8df70330fa9..5830c48988f 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs
@@ -1,30 +1,25 @@
 //! Constant evaluation details
 
-use std::{
-    collections::HashMap,
-    fmt::{Display, Write},
-};
-
-use chalk_ir::{BoundVar, DebruijnIndex, GenericArgData, IntTy, Scalar};
+use base_db::CrateId;
+use chalk_ir::{BoundVar, DebruijnIndex, GenericArgData};
 use hir_def::{
-    builtin_type::BuiltinInt,
-    expr::{ArithOp, BinaryOp, Expr, ExprId, Literal, Pat, PatId},
+    expr::Expr,
     path::ModPath,
-    resolver::{resolver_for_expr, ResolveValueResult, Resolver, ValueNs},
-    src::HasChildSource,
-    type_ref::ConstScalar,
-    ConstId, DefWithBodyId, EnumVariantId, Lookup,
+    resolver::{Resolver, ValueNs},
+    type_ref::ConstRef,
+    ConstId, EnumVariantId,
 };
-use la_arena::{Arena, Idx, RawIdx};
+use la_arena::{Idx, RawIdx};
 use stdx::never;
-use syntax::ast::HasName;
 
 use crate::{
-    db::HirDatabase, infer::InferenceContext, lower::ParamLoweringMode, to_placeholder_idx,
-    utils::Generics, Const, ConstData, ConstValue, GenericArg, InferenceResult, Interner, Ty,
-    TyBuilder, TyKind,
+    db::HirDatabase, infer::InferenceContext, layout::layout_of_ty, lower::ParamLoweringMode,
+    to_placeholder_idx, utils::Generics, Const, ConstData, ConstScalar, ConstValue, GenericArg,
+    Interner, MemoryMap, Ty, TyBuilder,
 };
 
+use super::mir::{interpret_mir, lower_to_mir, pad16, MirEvalError, MirLowerError};
+
 /// Extension trait for [`Const`]
 pub trait ConstExt {
     /// Is a [`Const`] unknown?
@@ -53,346 +48,24 @@ impl ConstExt for Const {
     }
 }
 
-pub struct ConstEvalCtx<'a> {
-    pub db: &'a dyn HirDatabase,
-    pub owner: DefWithBodyId,
-    pub exprs: &'a Arena<Expr>,
-    pub pats: &'a Arena<Pat>,
-    pub local_data: HashMap<PatId, ComputedExpr>,
-    infer: &'a InferenceResult,
-}
-
-impl ConstEvalCtx<'_> {
-    fn expr_ty(&mut self, expr: ExprId) -> Ty {
-        self.infer[expr].clone()
-    }
-}
-
 #[derive(Debug, Clone, PartialEq, Eq)]
 pub enum ConstEvalError {
-    NotSupported(&'static str),
-    SemanticError(&'static str),
-    Loop,
-    IncompleteExpr,
-    Panic(String),
-}
-
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub enum ComputedExpr {
-    Literal(Literal),
-    Enum(String, EnumVariantId, Literal),
-    Tuple(Box<[ComputedExpr]>),
+    MirLowerError(MirLowerError),
+    MirEvalError(MirEvalError),
 }
 
-impl Display for ComputedExpr {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        match self {
-            ComputedExpr::Literal(l) => match l {
-                Literal::Int(x, _) => {
-                    if *x >= 10 {
-                        write!(f, "{x} ({x:#X})")
-                    } else {
-                        x.fmt(f)
-                    }
-                }
-                Literal::Uint(x, _) => {
-                    if *x >= 10 {
-                        write!(f, "{x} ({x:#X})")
-                    } else {
-                        x.fmt(f)
-                    }
-                }
-                Literal::Float(x, _) => x.fmt(f),
-                Literal::Bool(x) => x.fmt(f),
-                Literal::Char(x) => std::fmt::Debug::fmt(x, f),
-                Literal::String(x) => std::fmt::Debug::fmt(x, f),
-                Literal::ByteString(x) => std::fmt::Debug::fmt(x, f),
-            },
-            ComputedExpr::Enum(name, _, _) => name.fmt(f),
-            ComputedExpr::Tuple(t) => {
-                f.write_char('(')?;
-                for x in &**t {
-                    x.fmt(f)?;
-                    f.write_str(", ")?;
-                }
-                f.write_char(')')
-            }
+impl From<MirLowerError> for ConstEvalError {
+    fn from(value: MirLowerError) -> Self {
+        match value {
+            MirLowerError::ConstEvalError(e) => *e,
+            _ => ConstEvalError::MirLowerError(value),
         }
     }
 }
 
-fn scalar_max(scalar: &Scalar) -> i128 {
-    match scalar {
-        Scalar::Bool => 1,
-        Scalar::Char => u32::MAX as i128,
-        Scalar::Int(x) => match x {
-            IntTy::Isize => isize::MAX as i128,
-            IntTy::I8 => i8::MAX as i128,
-            IntTy::I16 => i16::MAX as i128,
-            IntTy::I32 => i32::MAX as i128,
-            IntTy::I64 => i64::MAX as i128,
-            IntTy::I128 => i128::MAX,
-        },
-        Scalar::Uint(x) => match x {
-            chalk_ir::UintTy::Usize => usize::MAX as i128,
-            chalk_ir::UintTy::U8 => u8::MAX as i128,
-            chalk_ir::UintTy::U16 => u16::MAX as i128,
-            chalk_ir::UintTy::U32 => u32::MAX as i128,
-            chalk_ir::UintTy::U64 => u64::MAX as i128,
-            chalk_ir::UintTy::U128 => i128::MAX, // ignore too big u128 for now
-        },
-        Scalar::Float(_) => 0,
-    }
-}
-
-fn is_valid(scalar: &Scalar, value: i128) -> bool {
-    if value < 0 {
-        !matches!(scalar, Scalar::Uint(_)) && -scalar_max(scalar) - 1 <= value
-    } else {
-        value <= scalar_max(scalar)
-    }
-}
-
-fn get_name(ctx: &mut ConstEvalCtx<'_>, variant: EnumVariantId) -> String {
-    let loc = variant.parent.lookup(ctx.db.upcast());
-    let children = variant.parent.child_source(ctx.db.upcast());
-    let item_tree = loc.id.item_tree(ctx.db.upcast());
-
-    let variant_name = children.value[variant.local_id].name();
-    let enum_name = item_tree[loc.id.value].name.to_string();
-    enum_name + "::" + &variant_name.unwrap().to_string()
-}
-
-pub fn eval_const(
-    expr_id: ExprId,
-    ctx: &mut ConstEvalCtx<'_>,
-) -> Result<ComputedExpr, ConstEvalError> {
-    let u128_to_i128 = |it: u128| -> Result<i128, ConstEvalError> {
-        it.try_into().map_err(|_| ConstEvalError::NotSupported("u128 is too big"))
-    };
-
-    let expr = &ctx.exprs[expr_id];
-    match expr {
-        Expr::Missing => match ctx.owner {
-            // evaluate the implicit variant index of an enum variant without expression
-            // FIXME: This should return the type of the enum representation
-            DefWithBodyId::VariantId(variant) => {
-                let prev_idx: u32 = variant.local_id.into_raw().into();
-                let prev_idx = prev_idx.checked_sub(1).map(RawIdx::from).map(Idx::from_raw);
-                let value = match prev_idx {
-                    Some(local_id) => {
-                        let prev_variant = EnumVariantId { local_id, parent: variant.parent };
-                        1 + match ctx.db.const_eval_variant(prev_variant)? {
-                            ComputedExpr::Literal(Literal::Int(v, _)) => v,
-                            ComputedExpr::Literal(Literal::Uint(v, _)) => u128_to_i128(v)?,
-                            _ => {
-                                return Err(ConstEvalError::NotSupported(
-                                    "Enum can't contain this kind of value",
-                                ))
-                            }
-                        }
-                    }
-                    _ => 0,
-                };
-                Ok(ComputedExpr::Literal(Literal::Int(value, Some(BuiltinInt::I128))))
-            }
-            _ => Err(ConstEvalError::IncompleteExpr),
-        },
-        Expr::Literal(l) => Ok(ComputedExpr::Literal(l.clone())),
-        &Expr::UnaryOp { expr, op } => {
-            let ty = &ctx.expr_ty(expr);
-            let ev = eval_const(expr, ctx)?;
-            match op {
-                hir_def::expr::UnaryOp::Deref => Err(ConstEvalError::NotSupported("deref")),
-                hir_def::expr::UnaryOp::Not => {
-                    let v = match ev {
-                        ComputedExpr::Literal(Literal::Bool(b)) => {
-                            return Ok(ComputedExpr::Literal(Literal::Bool(!b)))
-                        }
-                        ComputedExpr::Literal(Literal::Int(v, _)) => v,
-                        ComputedExpr::Literal(Literal::Uint(v, _)) => u128_to_i128(v)?,
-                        _ => return Err(ConstEvalError::NotSupported("this kind of operator")),
-                    };
-                    let r = match ty.kind(Interner) {
-                        TyKind::Scalar(Scalar::Uint(x)) => match x {
-                            chalk_ir::UintTy::U8 => !(v as u8) as i128,
-                            chalk_ir::UintTy::U16 => !(v as u16) as i128,
-                            chalk_ir::UintTy::U32 => !(v as u32) as i128,
-                            chalk_ir::UintTy::U64 => !(v as u64) as i128,
-                            chalk_ir::UintTy::U128 => {
-                                return Err(ConstEvalError::NotSupported("negation of u128"))
-                            }
-                            chalk_ir::UintTy::Usize => !(v as usize) as i128,
-                        },
-                        TyKind::Scalar(Scalar::Int(x)) => match x {
-                            chalk_ir::IntTy::I8 => !(v as i8) as i128,
-                            chalk_ir::IntTy::I16 => !(v as i16) as i128,
-                            chalk_ir::IntTy::I32 => !(v as i32) as i128,
-                            chalk_ir::IntTy::I64 => !(v as i64) as i128,
-                            chalk_ir::IntTy::I128 => !v,
-                            chalk_ir::IntTy::Isize => !(v as isize) as i128,
-                        },
-                        _ => return Err(ConstEvalError::NotSupported("unreachable?")),
-                    };
-                    Ok(ComputedExpr::Literal(Literal::Int(r, None)))
-                }
-                hir_def::expr::UnaryOp::Neg => {
-                    let v = match ev {
-                        ComputedExpr::Literal(Literal::Int(v, _)) => v,
-                        ComputedExpr::Literal(Literal::Uint(v, _)) => u128_to_i128(v)?,
-                        _ => return Err(ConstEvalError::NotSupported("this kind of operator")),
-                    };
-                    Ok(ComputedExpr::Literal(Literal::Int(
-                        v.checked_neg().ok_or_else(|| {
-                            ConstEvalError::Panic("overflow in negation".to_string())
-                        })?,
-                        None,
-                    )))
-                }
-            }
-        }
-        &Expr::BinaryOp { lhs, rhs, op } => {
-            let ty = &ctx.expr_ty(lhs);
-            let lhs = eval_const(lhs, ctx)?;
-            let rhs = eval_const(rhs, ctx)?;
-            let op = op.ok_or(ConstEvalError::IncompleteExpr)?;
-            let v1 = match lhs {
-                ComputedExpr::Literal(Literal::Int(v, _)) => v,
-                ComputedExpr::Literal(Literal::Uint(v, _)) => u128_to_i128(v)?,
-                _ => return Err(ConstEvalError::NotSupported("this kind of operator")),
-            };
-            let v2 = match rhs {
-                ComputedExpr::Literal(Literal::Int(v, _)) => v,
-                ComputedExpr::Literal(Literal::Uint(v, _)) => u128_to_i128(v)?,
-                _ => return Err(ConstEvalError::NotSupported("this kind of operator")),
-            };
-            match op {
-                BinaryOp::ArithOp(b) => {
-                    let panic_arith = ConstEvalError::Panic(
-                        "attempt to run invalid arithmetic operation".to_string(),
-                    );
-                    let r = match b {
-                        ArithOp::Add => v1.checked_add(v2).ok_or_else(|| panic_arith.clone())?,
-                        ArithOp::Mul => v1.checked_mul(v2).ok_or_else(|| panic_arith.clone())?,
-                        ArithOp::Sub => v1.checked_sub(v2).ok_or_else(|| panic_arith.clone())?,
-                        ArithOp::Div => v1.checked_div(v2).ok_or_else(|| panic_arith.clone())?,
-                        ArithOp::Rem => v1.checked_rem(v2).ok_or_else(|| panic_arith.clone())?,
-                        ArithOp::Shl => v1
-                            .checked_shl(v2.try_into().map_err(|_| panic_arith.clone())?)
-                            .ok_or_else(|| panic_arith.clone())?,
-                        ArithOp::Shr => v1
-                            .checked_shr(v2.try_into().map_err(|_| panic_arith.clone())?)
-                            .ok_or_else(|| panic_arith.clone())?,
-                        ArithOp::BitXor => v1 ^ v2,
-                        ArithOp::BitOr => v1 | v2,
-                        ArithOp::BitAnd => v1 & v2,
-                    };
-                    if let TyKind::Scalar(s) = ty.kind(Interner) {
-                        if !is_valid(s, r) {
-                            return Err(panic_arith);
-                        }
-                    }
-                    Ok(ComputedExpr::Literal(Literal::Int(r, None)))
-                }
-                BinaryOp::LogicOp(_) => Err(ConstEvalError::SemanticError("logic op on numbers")),
-                _ => Err(ConstEvalError::NotSupported("bin op on this operators")),
-            }
-        }
-        Expr::Block { statements, tail, .. } => {
-            let mut prev_values = HashMap::<PatId, Option<ComputedExpr>>::default();
-            for statement in &**statements {
-                match *statement {
-                    hir_def::expr::Statement::Let { pat: pat_id, initializer, .. } => {
-                        let pat = &ctx.pats[pat_id];
-                        match pat {
-                            Pat::Bind { subpat, .. } if subpat.is_none() => (),
-                            _ => {
-                                return Err(ConstEvalError::NotSupported("complex patterns in let"))
-                            }
-                        };
-                        let value = match initializer {
-                            Some(x) => eval_const(x, ctx)?,
-                            None => continue,
-                        };
-                        if !prev_values.contains_key(&pat_id) {
-                            let prev = ctx.local_data.insert(pat_id, value);
-                            prev_values.insert(pat_id, prev);
-                        } else {
-                            ctx.local_data.insert(pat_id, value);
-                        }
-                    }
-                    hir_def::expr::Statement::Expr { .. } => {
-                        return Err(ConstEvalError::NotSupported("this kind of statement"))
-                    }
-                }
-            }
-            let r = match tail {
-                &Some(x) => eval_const(x, ctx),
-                None => Ok(ComputedExpr::Tuple(Box::new([]))),
-            };
-            // clean up local data, so caller will receive the exact map that passed to us
-            for (name, val) in prev_values {
-                match val {
-                    Some(x) => ctx.local_data.insert(name, x),
-                    None => ctx.local_data.remove(&name),
-                };
-            }
-            r
-        }
-        Expr::Path(p) => {
-            let resolver = resolver_for_expr(ctx.db.upcast(), ctx.owner, expr_id);
-            let pr = resolver
-                .resolve_path_in_value_ns(ctx.db.upcast(), p.mod_path())
-                .ok_or(ConstEvalError::SemanticError("unresolved path"))?;
-            let pr = match pr {
-                ResolveValueResult::ValueNs(v) => v,
-                ResolveValueResult::Partial(..) => {
-                    return match ctx
-                        .infer
-                        .assoc_resolutions_for_expr(expr_id)
-                        .ok_or(ConstEvalError::SemanticError("unresolved assoc item"))?
-                        .0
-                    {
-                        hir_def::AssocItemId::FunctionId(_) => {
-                            Err(ConstEvalError::NotSupported("assoc function"))
-                        }
-                        // FIXME use actual impl for trait assoc const
-                        hir_def::AssocItemId::ConstId(c) => ctx.db.const_eval(c),
-                        hir_def::AssocItemId::TypeAliasId(_) => {
-                            Err(ConstEvalError::NotSupported("assoc type alias"))
-                        }
-                    };
-                }
-            };
-            match pr {
-                ValueNs::LocalBinding(pat_id) => {
-                    let r = ctx
-                        .local_data
-                        .get(&pat_id)
-                        .ok_or(ConstEvalError::NotSupported("Unexpected missing local"))?;
-                    Ok(r.clone())
-                }
-                ValueNs::ConstId(id) => ctx.db.const_eval(id),
-                ValueNs::GenericParam(_) => {
-                    Err(ConstEvalError::NotSupported("const generic without substitution"))
-                }
-                ValueNs::EnumVariantId(id) => match ctx.db.const_eval_variant(id)? {
-                    ComputedExpr::Literal(lit) => {
-                        Ok(ComputedExpr::Enum(get_name(ctx, id), id, lit))
-                    }
-                    _ => Err(ConstEvalError::NotSupported(
-                        "Enums can't evalute to anything but numbers",
-                    )),
-                },
-                _ => Err(ConstEvalError::NotSupported("path that are not const or local")),
-            }
-        }
-        // FIXME: Handle the cast target
-        &Expr::Cast { expr, .. } => match eval_const(expr, ctx)? {
-            ComputedExpr::Enum(_, _, lit) => Ok(ComputedExpr::Literal(lit)),
-            _ => Err(ConstEvalError::NotSupported("Can't cast these types")),
-        },
-        _ => Err(ConstEvalError::NotSupported("This kind of expression")),
+impl From<MirEvalError> for ConstEvalError {
+    fn from(value: MirEvalError) -> Self {
+        ConstEvalError::MirEvalError(value)
     }
 }
 
@@ -449,68 +122,102 @@ pub fn intern_const_scalar(value: ConstScalar, ty: Ty) -> Const {
         .intern(Interner)
 }
 
+/// Interns a constant scalar with the given type
+pub fn intern_const_ref(db: &dyn HirDatabase, value: &ConstRef, ty: Ty, krate: CrateId) -> Const {
+    let bytes = match value {
+        ConstRef::Int(i) => {
+            // FIXME: We should handle failure of layout better.
+            let size = layout_of_ty(db, &ty, krate).map(|x| x.size.bytes_usize()).unwrap_or(16);
+            ConstScalar::Bytes(i.to_le_bytes()[0..size].to_vec(), MemoryMap::default())
+        }
+        ConstRef::UInt(i) => {
+            let size = layout_of_ty(db, &ty, krate).map(|x| x.size.bytes_usize()).unwrap_or(16);
+            ConstScalar::Bytes(i.to_le_bytes()[0..size].to_vec(), MemoryMap::default())
+        }
+        ConstRef::Bool(b) => ConstScalar::Bytes(vec![*b as u8], MemoryMap::default()),
+        ConstRef::Char(c) => {
+            ConstScalar::Bytes((*c as u32).to_le_bytes().to_vec(), MemoryMap::default())
+        }
+        ConstRef::Unknown => ConstScalar::Unknown,
+    };
+    intern_const_scalar(bytes, ty)
+}
+
 /// Interns a possibly-unknown target usize
-pub fn usize_const(value: Option<u128>) -> Const {
-    intern_const_scalar(value.map_or(ConstScalar::Unknown, ConstScalar::UInt), TyBuilder::usize())
+pub fn usize_const(db: &dyn HirDatabase, value: Option<u128>, krate: CrateId) -> Const {
+    intern_const_ref(
+        db,
+        &value.map_or(ConstRef::Unknown, ConstRef::UInt),
+        TyBuilder::usize(),
+        krate,
+    )
+}
+
+pub fn try_const_usize(c: &Const) -> Option<u128> {
+    match &c.data(Interner).value {
+        chalk_ir::ConstValue::BoundVar(_) => None,
+        chalk_ir::ConstValue::InferenceVar(_) => None,
+        chalk_ir::ConstValue::Placeholder(_) => None,
+        chalk_ir::ConstValue::Concrete(c) => match &c.interned {
+            ConstScalar::Bytes(x, _) => Some(u128::from_le_bytes(pad16(&x, false))),
+            _ => None,
+        },
+    }
 }
 
 pub(crate) fn const_eval_recover(
     _: &dyn HirDatabase,
     _: &[String],
     _: &ConstId,
-) -> Result<ComputedExpr, ConstEvalError> {
-    Err(ConstEvalError::Loop)
+) -> Result<Const, ConstEvalError> {
+    Err(ConstEvalError::MirLowerError(MirLowerError::Loop))
 }
 
-pub(crate) fn const_eval_variant_recover(
+pub(crate) fn const_eval_discriminant_recover(
     _: &dyn HirDatabase,
     _: &[String],
     _: &EnumVariantId,
-) -> Result<ComputedExpr, ConstEvalError> {
-    Err(ConstEvalError::Loop)
+) -> Result<i128, ConstEvalError> {
+    Err(ConstEvalError::MirLowerError(MirLowerError::Loop))
 }
 
-pub(crate) fn const_eval_variant_query(
+pub(crate) fn const_eval_query(
     db: &dyn HirDatabase,
     const_id: ConstId,
-) -> Result<ComputedExpr, ConstEvalError> {
+) -> Result<Const, ConstEvalError> {
     let def = const_id.into();
-    let body = db.body(def);
-    let infer = &db.infer(def);
-    let result = eval_const(
-        body.body_expr,
-        &mut ConstEvalCtx {
-            db,
-            owner: const_id.into(),
-            exprs: &body.exprs,
-            pats: &body.pats,
-            local_data: HashMap::default(),
-            infer,
-        },
-    );
-    result
+    let body = db.mir_body(def)?;
+    let c = interpret_mir(db, &body, false)?;
+    Ok(c)
 }
 
-pub(crate) fn const_eval_query_variant(
+pub(crate) fn const_eval_discriminant_variant(
     db: &dyn HirDatabase,
     variant_id: EnumVariantId,
-) -> Result<ComputedExpr, ConstEvalError> {
+) -> Result<i128, ConstEvalError> {
     let def = variant_id.into();
     let body = db.body(def);
-    let infer = &db.infer(def);
-    eval_const(
-        body.body_expr,
-        &mut ConstEvalCtx {
-            db,
-            owner: def,
-            exprs: &body.exprs,
-            pats: &body.pats,
-            local_data: HashMap::default(),
-            infer,
-        },
-    )
+    if body.exprs[body.body_expr] == Expr::Missing {
+        let prev_idx: u32 = variant_id.local_id.into_raw().into();
+        let prev_idx = prev_idx.checked_sub(1).map(RawIdx::from).map(Idx::from_raw);
+        let value = match prev_idx {
+            Some(local_id) => {
+                let prev_variant = EnumVariantId { local_id, parent: variant_id.parent };
+                1 + db.const_eval_discriminant(prev_variant)?
+            }
+            _ => 0,
+        };
+        return Ok(value);
+    }
+    let mir_body = db.mir_body(def)?;
+    let c = interpret_mir(db, &mir_body, false)?;
+    let c = try_const_usize(&c).unwrap() as i128;
+    Ok(c)
 }
 
+// FIXME: Ideally constants in const eval should have separate body (issue #7434), and this function should
+// get an `InferenceResult` instead of an `InferenceContext`. And we should remove `ctx.clone().resolve_all()` here
+// and make this function private. See the fixme comment on `InferenceContext::resolve_all`.
 pub(crate) fn eval_to_const(
     expr: Idx<Expr>,
     mode: ParamLoweringMode,
@@ -518,28 +225,20 @@ pub(crate) fn eval_to_const(
     args: impl FnOnce() -> Generics,
     debruijn: DebruijnIndex,
 ) -> Const {
+    let db = ctx.db;
     if let Expr::Path(p) = &ctx.body.exprs[expr] {
-        let db = ctx.db;
         let resolver = &ctx.resolver;
         if let Some(c) = path_to_const(db, resolver, p.mod_path(), mode, args, debruijn) {
             return c;
         }
     }
-    let body = ctx.body.clone();
-    let mut ctx = ConstEvalCtx {
-        db: ctx.db,
-        owner: ctx.owner,
-        exprs: &body.exprs,
-        pats: &body.pats,
-        local_data: HashMap::default(),
-        infer: &ctx.result,
-    };
-    let computed_expr = eval_const(expr, &mut ctx);
-    let const_scalar = match computed_expr {
-        Ok(ComputedExpr::Literal(literal)) => literal.into(),
-        _ => ConstScalar::Unknown,
-    };
-    intern_const_scalar(const_scalar, TyBuilder::usize())
+    let infer = ctx.clone().resolve_all();
+    if let Ok(mir_body) = lower_to_mir(ctx.db, ctx.owner, &ctx.body, &infer, expr) {
+        if let Ok(result) = interpret_mir(db, &mir_body, true) {
+            return result;
+        }
+    }
+    unknown_const(infer[expr].clone())
 }
 
 #[cfg(test)]
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs
index 3c930c077b3..6a29e8ce52e 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs
@@ -1,24 +1,44 @@
 use base_db::fixture::WithFixture;
-use hir_def::{db::DefDatabase, expr::Literal};
+use hir_def::db::DefDatabase;
 
-use crate::{consteval::ComputedExpr, db::HirDatabase, test_db::TestDB};
+use crate::{
+    consteval::try_const_usize, db::HirDatabase, test_db::TestDB, Const, ConstScalar, Interner,
+};
 
-use super::ConstEvalError;
+use super::{
+    super::mir::{MirEvalError, MirLowerError},
+    ConstEvalError,
+};
 
+fn simplify(e: ConstEvalError) -> ConstEvalError {
+    match e {
+        ConstEvalError::MirEvalError(MirEvalError::InFunction(_, e)) => {
+            simplify(ConstEvalError::MirEvalError(*e))
+        }
+        _ => e,
+    }
+}
+
+#[track_caller]
 fn check_fail(ra_fixture: &str, error: ConstEvalError) {
-    assert_eq!(eval_goal(ra_fixture), Err(error));
+    assert_eq!(eval_goal(ra_fixture).map_err(simplify), Err(error));
 }
 
+#[track_caller]
 fn check_number(ra_fixture: &str, answer: i128) {
     let r = eval_goal(ra_fixture).unwrap();
-    match r {
-        ComputedExpr::Literal(Literal::Int(r, _)) => assert_eq!(r, answer),
-        ComputedExpr::Literal(Literal::Uint(r, _)) => assert_eq!(r, answer as u128),
-        x => panic!("Expected number but found {x:?}"),
+    match &r.data(Interner).value {
+        chalk_ir::ConstValue::Concrete(c) => match &c.interned {
+            ConstScalar::Bytes(b, _) => {
+                assert_eq!(b, &answer.to_le_bytes()[0..b.len()]);
+            }
+            x => panic!("Expected number but found {:?}", x),
+        },
+        _ => panic!("result of const eval wasn't a concrete const"),
     }
 }
 
-fn eval_goal(ra_fixture: &str) -> Result<ComputedExpr, ConstEvalError> {
+fn eval_goal(ra_fixture: &str) -> Result<Const, ConstEvalError> {
     let (db, file_id) = TestDB::with_single_file(ra_fixture);
     let module_id = db.module_for_file(file_id);
     let def_map = module_id.def_map(&db);
@@ -42,21 +62,18 @@ fn eval_goal(ra_fixture: &str) -> Result<ComputedExpr, ConstEvalError> {
 #[test]
 fn add() {
     check_number(r#"const GOAL: usize = 2 + 2;"#, 4);
+    check_number(r#"const GOAL: i32 = -2 + --5;"#, 3);
+    check_number(r#"const GOAL: i32 = 7 - 5;"#, 2);
+    check_number(r#"const GOAL: i32 = 7 + (1 - 5);"#, 3);
 }
 
 #[test]
 fn bit_op() {
     check_number(r#"const GOAL: u8 = !0 & !(!0 >> 1)"#, 128);
     check_number(r#"const GOAL: i8 = !0 & !(!0 >> 1)"#, 0);
-    // FIXME: rustc evaluate this to -128
-    check_fail(
-        r#"const GOAL: i8 = 1 << 7"#,
-        ConstEvalError::Panic("attempt to run invalid arithmetic operation".to_string()),
-    );
-    check_fail(
-        r#"const GOAL: i8 = 1 << 8"#,
-        ConstEvalError::Panic("attempt to run invalid arithmetic operation".to_string()),
-    );
+    check_number(r#"const GOAL: i8 = 1 << 7"#, (1i8 << 7) as i128);
+    // FIXME: report panic here
+    check_number(r#"const GOAL: i8 = 1 << 8"#, 0);
 }
 
 #[test]
@@ -74,6 +91,803 @@ fn locals() {
 }
 
 #[test]
+fn references() {
+    check_number(
+        r#"
+    const GOAL: usize = {
+        let x = 3;
+        let y = &mut x;
+        *y = 5;
+        x
+    };
+    "#,
+        5,
+    );
+    check_number(
+        r#"
+    struct Foo(i32);
+    impl Foo {
+        fn method(&mut self, x: i32) {
+            self.0 = 2 * self.0 + x;
+        }
+    }
+    const GOAL: i32 = {
+        let mut x = Foo(3);
+        x.method(5);
+        x.0
+    };
+    "#,
+        11,
+    );
+}
+
+#[test]
+fn reference_autoderef() {
+    check_number(
+        r#"
+    const GOAL: usize = {
+        let x = 3;
+        let y = &mut x;
+        let y: &mut usize = &mut y;
+        *y = 5;
+        x
+    };
+    "#,
+        5,
+    );
+    check_number(
+        r#"
+    const GOAL: usize = {
+        let x = 3;
+        let y = &&&&&&&x;
+        let z: &usize = &y;
+        *z
+    };
+    "#,
+        3,
+    );
+    check_number(
+        r#"
+    struct Foo<T> { x: T }
+    impl<T> Foo<T> {
+        fn foo(&mut self) -> T { self.x }
+    }
+    fn f(i: &mut &mut Foo<Foo<i32>>) -> i32 {
+        ((**i).x).foo()
+    }
+    fn g(i: Foo<Foo<i32>>) -> i32 {
+        i.x.foo()
+    }
+    const GOAL: i32 = f(&mut &mut Foo { x: Foo { x: 3 } }) + g(Foo { x: Foo { x: 5 } });
+    "#,
+        8,
+    );
+}
+
+#[test]
+fn overloaded_deref() {
+    // FIXME: We should support this.
+    check_fail(
+        r#"
+    //- minicore: deref_mut
+    struct Foo;
+
+    impl core::ops::Deref for Foo {
+        type Target = i32;
+        fn deref(&self) -> &i32 {
+            &5
+        }
+    }
+
+    const GOAL: i32 = {
+        let x = Foo;
+        let y = &*x;
+        *y + *x
+    };
+    "#,
+        ConstEvalError::MirLowerError(MirLowerError::NotSupported(
+            "explicit overloaded deref".into(),
+        )),
+    );
+}
+
+#[test]
+fn overloaded_deref_autoref() {
+    check_number(
+        r#"
+    //- minicore: deref_mut
+    struct Foo;
+    struct Bar;
+
+    impl core::ops::Deref for Foo {
+        type Target = Bar;
+        fn deref(&self) -> &Bar {
+            &Bar
+        }
+    }
+
+    impl Bar {
+        fn method(&self) -> i32 {
+            5
+        }
+    }
+
+    const GOAL: i32 = Foo.method();
+    "#,
+        5,
+    );
+}
+
+#[test]
+fn function_call() {
+    check_number(
+        r#"
+    const fn f(x: usize) -> usize {
+        2 * x + 5
+    }
+    const GOAL: usize = f(3);
+    "#,
+        11,
+    );
+    check_number(
+        r#"
+    const fn add(x: usize, y: usize) -> usize {
+        x + y
+    }
+    const GOAL: usize = add(add(1, 2), add(3, add(4, 5)));
+    "#,
+        15,
+    );
+}
+
+#[test]
+fn intrinsics() {
+    check_number(
+        r#"
+    extern "rust-intrinsic" {
+        pub fn size_of<T>() -> usize;
+    }
+
+    const GOAL: usize = size_of::<i32>();
+    "#,
+        4,
+    );
+}
+
+#[test]
+fn trait_basic() {
+    check_number(
+        r#"
+    trait Foo {
+        fn f(&self) -> u8;
+    }
+
+    impl Foo for u8 {
+        fn f(&self) -> u8 {
+            *self + 33
+        }
+    }
+
+    const GOAL: u8 = {
+        let x = 3;
+        Foo::f(&x)
+    };
+    "#,
+        36,
+    );
+}
+
+#[test]
+fn trait_method() {
+    check_number(
+        r#"
+    trait Foo {
+        fn f(&self) -> u8;
+    }
+
+    impl Foo for u8 {
+        fn f(&self) -> u8 {
+            *self + 33
+        }
+    }
+
+    const GOAL: u8 = {
+        let x = 3;
+        x.f()
+    };
+    "#,
+        36,
+    );
+}
+
+#[test]
+fn generic_fn() {
+    check_number(
+        r#"
+    trait Foo {
+        fn f(&self) -> u8;
+    }
+
+    impl Foo for () {
+        fn f(&self) -> u8 {
+            0
+        }
+    }
+
+    struct Succ<S>(S);
+
+    impl<T: Foo> Foo for Succ<T> {
+        fn f(&self) -> u8 {
+            self.0.f() + 1
+        }
+    }
+
+    const GOAL: u8 = Succ(Succ(())).f();
+    "#,
+        2,
+    );
+    check_number(
+        r#"
+    trait Foo {
+        fn f(&self) -> u8;
+    }
+
+    impl Foo for u8 {
+        fn f(&self) -> u8 {
+            *self + 33
+        }
+    }
+
+    fn foof<T: Foo>(x: T, y: T) -> u8 {
+        x.f() + y.f()
+    }
+
+    const GOAL: u8 = foof(2, 5);
+    "#,
+        73,
+    );
+    check_number(
+        r#"
+    fn bar<A, B>(a: A, b: B) -> B {
+        b
+    }
+        const GOAL: u8 = bar("hello", 12);
+        "#,
+        12,
+    );
+    check_number(
+        r#"
+    //- minicore: coerce_unsized, index, slice
+    fn bar<A, B>(a: A, b: B) -> B {
+        b
+    }
+    fn foo<T>(x: [T; 2]) -> T {
+        bar(x[0], x[1])
+    }
+
+    const GOAL: u8 = foo([2, 5]);
+    "#,
+        5,
+    );
+}
+
+#[test]
+fn impl_trait() {
+    check_number(
+        r#"
+    trait Foo {
+        fn f(&self) -> u8;
+    }
+
+    impl Foo for u8 {
+        fn f(&self) -> u8 {
+            *self + 33
+        }
+    }
+
+    fn foof(x: impl Foo, y: impl Foo) -> impl Foo {
+        x.f() + y.f()
+    }
+
+    const GOAL: u8 = foof(2, 5).f();
+    "#,
+        106,
+    );
+    check_number(
+        r#"
+        struct Foo<T>(T, T, (T, T));
+        trait S {
+            fn sum(&self) -> i64;
+        }
+        impl S for i64 {
+            fn sum(&self) -> i64 {
+                *self
+            }
+        }
+        impl<T: S> S for Foo<T> {
+            fn sum(&self) -> i64 {
+                self.0.sum() + self.1.sum() + self.2 .0.sum() + self.2 .1.sum()
+            }
+        }
+
+        fn foo() -> Foo<impl S> {
+            Foo(
+                Foo(1i64, 2, (3, 4)),
+                Foo(5, 6, (7, 8)),
+                (
+                    Foo(9, 10, (11, 12)),
+                    Foo(13, 14, (15, 16)),
+                ),
+            )
+        }
+        const GOAL: i64 = foo().sum();
+    "#,
+        136,
+    );
+}
+
+#[test]
+fn ifs() {
+    check_number(
+        r#"
+    const fn f(b: bool) -> u8 {
+        if b { 1 } else { 10 }
+    }
+
+    const GOAL: u8 = f(true) + f(true) + f(false);
+        "#,
+        12,
+    );
+    check_number(
+        r#"
+    const fn max(a: i32, b: i32) -> i32 {
+        if a < b { b } else { a }
+    }
+
+    const GOAL: i32 = max(max(1, max(10, 3)), 0-122);
+        "#,
+        10,
+    );
+
+    check_number(
+        r#"
+    const fn max(a: &i32, b: &i32) -> &i32 {
+        if *a < *b { b } else { a }
+    }
+
+    const GOAL: i32 = *max(max(&1, max(&10, &3)), &5);
+        "#,
+        10,
+    );
+}
+
+#[test]
+fn loops() {
+    check_number(
+        r#"
+    const GOAL: u8 = {
+        let mut x = 0;
+        loop {
+            x = x + 1;
+            while true {
+                break;
+            }
+            x = x + 1;
+            if x == 2 {
+                continue;
+            }
+            break;
+        }
+        x
+    };
+        "#,
+        4,
+    );
+}
+
+#[test]
+fn for_loops() {
+    check_number(
+        r#"
+    //- minicore: iterator
+
+    struct Range {
+        start: u8,
+        end: u8,
+    }
+
+    impl Iterator for Range {
+        type Item = u8;
+        fn next(&mut self) -> Option<u8> {
+            if self.start >= self.end {
+                None
+            } else {
+                let r = self.start;
+                self.start = self.start + 1;
+                Some(r)
+            }
+        }
+    }
+
+    const GOAL: u8 = {
+        let mut sum = 0;
+        let ar = Range { start: 1, end: 11 };
+        for i in ar {
+            sum = sum + i;
+        }
+        sum
+    };
+        "#,
+        55,
+    );
+}
+
+#[test]
+fn recursion() {
+    check_number(
+        r#"
+    const fn fact(k: i32) -> i32 {
+        if k > 0 { fact(k - 1) * k } else { 1 }
+    }
+
+    const GOAL: i32 = fact(5);
+        "#,
+        120,
+    );
+}
+
+#[test]
+fn structs() {
+    check_number(
+        r#"
+        struct Point {
+            x: i32,
+            y: i32,
+        }
+
+        const GOAL: i32 = {
+            let p = Point { x: 5, y: 2 };
+            let y = 1;
+            let x = 3;
+            let q = Point { y, x };
+            p.x + p.y + p.x + q.y + q.y + q.x
+        };
+        "#,
+        17,
+    );
+}
+
+#[test]
+fn unions() {
+    check_number(
+        r#"
+        union U {
+            f1: i64,
+            f2: (i32, i32),
+        }
+
+        const GOAL: i32 = {
+            let p = U { f1: 0x0123ABCD0123DCBA };
+            let p = unsafe { p.f2 };
+            p.0 + p.1 + p.1
+        };
+        "#,
+        0x0123ABCD * 2 + 0x0123DCBA,
+    );
+}
+
+#[test]
+fn tuples() {
+    check_number(
+        r#"
+    const GOAL: u8 = {
+        let a = (10, 20, 3, 15);
+        a.1
+    };
+        "#,
+        20,
+    );
+    check_number(
+        r#"
+    const GOAL: u8 = {
+        let mut a = (10, 20, 3, 15);
+        a.1 = 2;
+        a.0 + a.1 + a.2 + a.3
+    };
+        "#,
+        30,
+    );
+    check_number(
+        r#"
+    struct TupleLike(i32, u8, i64, u16);
+    const GOAL: u8 = {
+        let a = TupleLike(10, 20, 3, 15);
+        a.1
+    };
+        "#,
+        20,
+    );
+    check_number(
+        r#"
+    const GOAL: u8 = {
+        match (&(2 + 2), &4) {
+            (left_val, right_val) => {
+                if !(*left_val == *right_val) {
+                    2
+                } else {
+                    5
+                }
+            }
+        }
+    };
+        "#,
+        5,
+    );
+}
+
+#[test]
+fn path_pattern_matching() {
+    check_number(
+        r#"
+    enum Season {
+        Spring,
+        Summer,
+        Fall,
+        Winter,
+    }
+
+    use Season::*;
+
+    const fn f(x: Season) -> i32 {
+        match x {
+            Spring => 1,
+            Summer => 2,
+            Fall => 3,
+            Winter => 4,
+        }
+    }
+    const GOAL: i32 = f(Spring) + 10 * f(Summer) + 100 * f(Fall) + 1000 * f(Winter);
+        "#,
+        4321,
+    );
+}
+
+#[test]
+fn pattern_matching_ergonomics() {
+    check_number(
+        r#"
+    const fn f(x: &(u8, u8)) -> u8 {
+        match x {
+            (a, b) => *a + *b
+        }
+    }
+    const GOAL: u8 = f(&(2, 3));
+        "#,
+        5,
+    );
+}
+
+#[test]
+fn let_else() {
+    check_number(
+        r#"
+    const fn f(x: &(u8, u8)) -> u8 {
+        let (a, b) = x;
+        *a + *b
+    }
+    const GOAL: u8 = f(&(2, 3));
+        "#,
+        5,
+    );
+    check_number(
+        r#"
+    enum SingleVariant {
+        Var(u8, u8),
+    }
+    const fn f(x: &&&&&SingleVariant) -> u8 {
+        let SingleVariant::Var(a, b) = x;
+        *a + *b
+    }
+    const GOAL: u8 = f(&&&&&SingleVariant::Var(2, 3));
+        "#,
+        5,
+    );
+    check_number(
+        r#"
+    //- minicore: option
+    const fn f(x: Option<i32>) -> i32 {
+        let Some(x) = x else { return 10 };
+        2 * x
+    }
+    const GOAL: i32 = f(Some(1000)) + f(None);
+        "#,
+        2010,
+    );
+}
+
+#[test]
+fn function_param_patterns() {
+    check_number(
+        r#"
+    const fn f((a, b): &(u8, u8)) -> u8 {
+        *a + *b
+    }
+    const GOAL: u8 = f(&(2, 3));
+        "#,
+        5,
+    );
+    check_number(
+        r#"
+    const fn f(c @ (a, b): &(u8, u8)) -> u8 {
+        *a + *b + c.0 + (*c).1
+    }
+    const GOAL: u8 = f(&(2, 3));
+        "#,
+        10,
+    );
+    check_number(
+        r#"
+    const fn f(ref a: u8) -> u8 {
+        *a
+    }
+    const GOAL: u8 = f(2);
+        "#,
+        2,
+    );
+    check_number(
+        r#"
+    struct Foo(u8);
+    impl Foo {
+        const fn f(&self, (a, b): &(u8, u8)) -> u8 {
+            self.0 + *a + *b
+        }
+    }
+    const GOAL: u8 = Foo(4).f(&(2, 3));
+        "#,
+        9,
+    );
+}
+
+#[test]
+fn options() {
+    check_number(
+        r#"
+    //- minicore: option
+    const GOAL: u8 = {
+        let x = Some(2);
+        match x {
+            Some(y) => 2 * y,
+            _ => 10,
+        }
+    };
+        "#,
+        4,
+    );
+    check_number(
+        r#"
+    //- minicore: option
+    fn f(x: Option<Option<i32>>) -> i32 {
+        if let Some(y) = x && let Some(z) = y {
+            z
+        } else if let Some(y) = x {
+            1
+        } else {
+            0
+        }
+    }
+    const GOAL: i32 = f(Some(Some(10))) + f(Some(None)) + f(None);
+        "#,
+        11,
+    );
+    check_number(
+        r#"
+    //- minicore: option
+    const GOAL: u8 = {
+        let x = None;
+        match x {
+            Some(y) => 2 * y,
+            _ => 10,
+        }
+    };
+        "#,
+        10,
+    );
+    check_number(
+        r#"
+    //- minicore: option
+    const GOAL: Option<&u8> = None;
+        "#,
+        0,
+    );
+}
+
+#[test]
+fn or_pattern() {
+    check_number(
+        r#"
+    const GOAL: u8 = {
+        let (a | a) = 2;
+        a
+    };
+        "#,
+        2,
+    );
+    check_number(
+        r#"
+    //- minicore: option
+    const fn f(x: Option<i32>) -> i32 {
+        let (Some(a) | Some(a)) = x else { return 2; };
+        a
+    }
+    const GOAL: i32 = f(Some(10)) + f(None);
+        "#,
+        12,
+    );
+    check_number(
+        r#"
+    //- minicore: option
+    const fn f(x: Option<i32>, y: Option<i32>) -> i32 {
+        match (x, y) {
+            (Some(x), Some(y)) => x * y,
+            (Some(a), _) | (_, Some(a)) => a,
+            _ => 10,
+        }
+    }
+    const GOAL: i32 = f(Some(10), Some(20)) + f(Some(30), None) + f(None, Some(40)) + f(None, None);
+        "#,
+        280,
+    );
+}
+
+#[test]
+fn array_and_index() {
+    check_number(
+        r#"
+    //- minicore: coerce_unsized, index, slice
+    const GOAL: u8 = {
+        let a = [10, 20, 3, 15];
+        let x: &[u8] = &a;
+        x[1]
+    };
+        "#,
+        20,
+    );
+    check_number(
+        r#"
+    //- minicore: coerce_unsized, index, slice
+    const GOAL: usize = [1, 2, 3][2];"#,
+        3,
+    );
+    check_number(
+        r#"
+    //- minicore: coerce_unsized, index, slice
+    const GOAL: usize = { let a = [1, 2, 3]; let x: &[i32] = &a; x.len() };"#,
+        3,
+    );
+    check_number(
+        r#"
+    //- minicore: coerce_unsized, index, slice
+    const GOAL: usize = [1, 2, 3, 4, 5].len();"#,
+        5,
+    );
+}
+
+#[test]
+fn byte_string() {
+    check_number(
+        r#"
+    //- minicore: coerce_unsized, index, slice
+    const GOAL: u8 = {
+        let a = b"hello";
+        let x: &[u8] = a;
+        x[0]
+    };
+        "#,
+        104,
+    );
+}
+
+#[test]
 fn consts() {
     check_number(
         r#"
@@ -92,41 +906,35 @@ fn enums() {
         r#"
     enum E {
         F1 = 1,
-        F2 = 2 * E::F1 as u8,
-        F3 = 3 * E::F2 as u8,
+        F2 = 2 * E::F1 as isize, // Rustc expects an isize here
+        F3 = 3 * E::F2 as isize,
     }
-    const GOAL: i32 = E::F3 as u8;
+    const GOAL: u8 = E::F3 as u8;
     "#,
         6,
     );
     check_number(
         r#"
     enum E { F1 = 1, F2, }
-    const GOAL: i32 = E::F2 as u8;
+    const GOAL: u8 = E::F2 as u8;
     "#,
         2,
     );
     check_number(
         r#"
     enum E { F1, }
-    const GOAL: i32 = E::F1 as u8;
+    const GOAL: u8 = E::F1 as u8;
     "#,
         0,
     );
     let r = eval_goal(
         r#"
-        enum E { A = 1, }
+        enum E { A = 1, B }
         const GOAL: E = E::A;
         "#,
     )
     .unwrap();
-    match r {
-        ComputedExpr::Enum(name, _, Literal::Uint(val, _)) => {
-            assert_eq!(name, "E::A");
-            assert_eq!(val, 1);
-        }
-        x => panic!("Expected enum but found {x:?}"),
-    }
+    assert_eq!(try_const_usize(&r), Some(1));
 }
 
 #[test]
@@ -138,7 +946,19 @@ fn const_loop() {
     const F2: i32 = 2 * F1;
     const GOAL: i32 = F3;
     "#,
-        ConstEvalError::Loop,
+        ConstEvalError::MirLowerError(MirLowerError::Loop),
+    );
+}
+
+#[test]
+fn const_transfer_memory() {
+    check_number(
+        r#"
+    const A1: &i32 = &2;
+    const A2: &i32 = &5;
+    const GOAL: i32 = *A1 + *A2;
+    "#,
+        7,
     );
 }
 
@@ -157,7 +977,20 @@ fn const_impl_assoc() {
 }
 
 #[test]
-fn const_generic_subst() {
+fn const_generic_subst_fn() {
+    check_number(
+        r#"
+    const fn f<const A: usize>(x: usize) -> usize {
+        A * x + 5
+    }
+    const GOAL: usize = f::<2>(3);
+    "#,
+        11,
+    );
+}
+
+#[test]
+fn const_generic_subst_assoc_const_impl() {
     // FIXME: this should evaluate to 5
     check_fail(
         r#"
@@ -167,7 +1000,7 @@ fn const_generic_subst() {
     }
     const GOAL: usize = Adder::<2, 3>::VAL;
     "#,
-        ConstEvalError::NotSupported("const generic without substitution"),
+        ConstEvalError::MirEvalError(MirEvalError::TypeError("missing generic arg")),
     );
 }
 
@@ -185,6 +1018,58 @@ fn const_trait_assoc() {
     }
     const GOAL: usize = U0::VAL;
     "#,
-        ConstEvalError::IncompleteExpr,
+        ConstEvalError::MirLowerError(MirLowerError::IncompleteExpr),
+    );
+}
+
+#[test]
+fn exec_limits() {
+    check_fail(
+        r#"
+    const GOAL: usize = loop {};
+    "#,
+        ConstEvalError::MirEvalError(MirEvalError::ExecutionLimitExceeded),
+    );
+    check_fail(
+        r#"
+    const fn f(x: i32) -> i32 {
+        f(x + 1)
+    }
+    const GOAL: i32 = f(0);
+    "#,
+        ConstEvalError::MirEvalError(MirEvalError::StackOverflow),
+    );
+    // Reasonable code should still work
+    check_number(
+        r#"
+    const fn nth_odd(n: i32) -> i32 {
+        2 * n - 1
+    }
+    const fn f(n: i32) -> i32 {
+        let sum = 0;
+        let i = 0;
+        while i < n {
+            i = i + 1;
+            sum = sum + nth_odd(i);
+        }
+        sum
+    }
+    const GOAL: i32 = f(10000);
+    "#,
+        10000 * 10000,
+    );
+}
+
+#[test]
+fn type_error() {
+    let e = eval_goal(
+        r#"
+    const GOAL: u8 = {
+        let x: u16 = 2;
+        let y: (u8, u8) = x;
+        y.0
+    };
+    "#,
     );
+    assert!(matches!(e, Err(ConstEvalError::MirLowerError(MirLowerError::TypeMismatch(_)))));
 }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs
index d45e2a943ad..304c78767f1 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs
@@ -16,10 +16,12 @@ use smallvec::SmallVec;
 
 use crate::{
     chalk_db,
-    consteval::{ComputedExpr, ConstEvalError},
+    consteval::ConstEvalError,
     method_resolution::{InherentImpls, TraitImpls, TyFingerprint},
-    Binders, CallableDefId, FnDefId, GenericArg, ImplTraitId, InferenceResult, Interner, PolyFnSig,
-    QuantifiedWhereClause, ReturnTypeImplTraits, Substitution, TraitRef, Ty, TyDefId, ValueTyDefId,
+    mir::{BorrowckResult, MirBody, MirLowerError},
+    Binders, CallableDefId, Const, FnDefId, GenericArg, ImplTraitId, InferenceResult, Interner,
+    PolyFnSig, QuantifiedWhereClause, ReturnTypeImplTraits, Substitution, TraitRef, Ty, TyDefId,
+    ValueTyDefId,
 };
 use hir_expand::name::Name;
 
@@ -32,6 +34,13 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
     #[salsa::invoke(crate::infer::infer_query)]
     fn infer_query(&self, def: DefWithBodyId) -> Arc<InferenceResult>;
 
+    #[salsa::invoke(crate::mir::mir_body_query)]
+    #[salsa::cycle(crate::mir::mir_body_recover)]
+    fn mir_body(&self, def: DefWithBodyId) -> Result<Arc<MirBody>, MirLowerError>;
+
+    #[salsa::invoke(crate::mir::borrowck_query)]
+    fn borrowck(&self, def: DefWithBodyId) -> Result<Arc<BorrowckResult>, MirLowerError>;
+
     #[salsa::invoke(crate::lower::ty_query)]
     #[salsa::cycle(crate::lower::ty_recover)]
     fn ty(&self, def: TyDefId) -> Binders<Ty>;
@@ -46,13 +55,13 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
     #[salsa::invoke(crate::lower::const_param_ty_query)]
     fn const_param_ty(&self, def: ConstParamId) -> Ty;
 
-    #[salsa::invoke(crate::consteval::const_eval_variant_query)]
+    #[salsa::invoke(crate::consteval::const_eval_query)]
     #[salsa::cycle(crate::consteval::const_eval_recover)]
-    fn const_eval(&self, def: ConstId) -> Result<ComputedExpr, ConstEvalError>;
+    fn const_eval(&self, def: ConstId) -> Result<Const, ConstEvalError>;
 
-    #[salsa::invoke(crate::consteval::const_eval_query_variant)]
-    #[salsa::cycle(crate::consteval::const_eval_variant_recover)]
-    fn const_eval_variant(&self, def: EnumVariantId) -> Result<ComputedExpr, ConstEvalError>;
+    #[salsa::invoke(crate::consteval::const_eval_discriminant_variant)]
+    #[salsa::cycle(crate::consteval::const_eval_discriminant_recover)]
+    fn const_eval_discriminant(&self, def: EnumVariantId) -> Result<i128, ConstEvalError>;
 
     #[salsa::invoke(crate::lower::impl_trait_query)]
     fn impl_trait(&self, def: ImplId) -> Option<Binders<TraitRef>>;
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs
index f7031a8546a..d36b93e3bdd 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs
@@ -178,6 +178,7 @@ impl<'a> DeclValidator<'a> {
                 AttrDefId::StaticId(sid) => Some(sid.lookup(self.db.upcast()).container.into()),
                 AttrDefId::ConstId(cid) => Some(cid.lookup(self.db.upcast()).container.into()),
                 AttrDefId::TraitId(tid) => Some(tid.lookup(self.db.upcast()).container.into()),
+                AttrDefId::TraitAliasId(taid) => Some(taid.lookup(self.db.upcast()).container.into()),
                 AttrDefId::ImplId(iid) => Some(iid.lookup(self.db.upcast()).container.into()),
                 AttrDefId::ExternBlockId(id) => Some(id.lookup(self.db.upcast()).container.into()),
                 // These warnings should not explore macro definitions at all
@@ -234,8 +235,8 @@ impl<'a> DeclValidator<'a> {
         let pats_replacements = body
             .pats
             .iter()
-            .filter_map(|(id, pat)| match pat {
-                Pat::Bind { name, .. } => Some((id, name)),
+            .filter_map(|(pat_id, pat)| match pat {
+                Pat::Bind { id, .. } => Some((pat_id, &body.bindings[*id].name)),
                 _ => None,
             })
             .filter_map(|(id, bind_name)| {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs
index 3286dcb5afd..2e9066788cf 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs
@@ -5,11 +5,11 @@
 use std::fmt;
 use std::sync::Arc;
 
+use either::Either;
 use hir_def::lang_item::LangItem;
 use hir_def::{resolver::HasResolver, AdtId, AssocItemId, DefWithBodyId, HasModule};
 use hir_def::{ItemContainerId, Lookup};
 use hir_expand::name;
-use itertools::Either;
 use itertools::Itertools;
 use rustc_hash::FxHashSet;
 use typed_arena::Arena;
@@ -84,7 +84,7 @@ impl ExprValidator {
 
             match expr {
                 Expr::Match { expr, arms } => {
-                    self.validate_match(id, *expr, arms, db, self.infer.clone());
+                    self.validate_match(id, *expr, arms, db);
                 }
                 Expr::Call { .. } | Expr::MethodCall { .. } => {
                     self.validate_call(db, id, expr, &mut filter_map_next_checker);
@@ -147,16 +147,15 @@ impl ExprValidator {
 
     fn validate_match(
         &mut self,
-        id: ExprId,
         match_expr: ExprId,
+        scrutinee_expr: ExprId,
         arms: &[MatchArm],
         db: &dyn HirDatabase,
-        infer: Arc<InferenceResult>,
     ) {
         let body = db.body(self.owner);
 
-        let match_expr_ty = &infer[match_expr];
-        if match_expr_ty.is_unknown() {
+        let scrut_ty = &self.infer[scrutinee_expr];
+        if scrut_ty.is_unknown() {
             return;
         }
 
@@ -166,23 +165,23 @@ impl ExprValidator {
         let mut m_arms = Vec::with_capacity(arms.len());
         let mut has_lowering_errors = false;
         for arm in arms {
-            if let Some(pat_ty) = infer.type_of_pat.get(arm.pat) {
+            if let Some(pat_ty) = self.infer.type_of_pat.get(arm.pat) {
                 // We only include patterns whose type matches the type
-                // of the match expression. If we had an InvalidMatchArmPattern
+                // of the scrutinee expression. If we had an InvalidMatchArmPattern
                 // diagnostic or similar we could raise that in an else
                 // block here.
                 //
                 // When comparing the types, we also have to consider that rustc
-                // will automatically de-reference the match expression type if
+                // will automatically de-reference the scrutinee expression type if
                 // necessary.
                 //
                 // FIXME we should use the type checker for this.
-                if (pat_ty == match_expr_ty
-                    || match_expr_ty
+                if (pat_ty == scrut_ty
+                    || scrut_ty
                         .as_reference()
                         .map(|(match_expr_ty, ..)| match_expr_ty == pat_ty)
                         .unwrap_or(false))
-                    && types_of_subpatterns_do_match(arm.pat, &body, &infer)
+                    && types_of_subpatterns_do_match(arm.pat, &body, &self.infer)
                 {
                     // If we had a NotUsefulMatchArm diagnostic, we could
                     // check the usefulness of each pattern as we added it
@@ -206,7 +205,7 @@ impl ExprValidator {
             return;
         }
 
-        let report = compute_match_usefulness(&cx, &m_arms, match_expr_ty);
+        let report = compute_match_usefulness(&cx, &m_arms, scrut_ty);
 
         // FIXME Report unreacheble arms
         // https://github.com/rust-lang/rust/blob/f31622a50/compiler/rustc_mir_build/src/thir/pattern/check_match.rs#L200
@@ -214,8 +213,8 @@ impl ExprValidator {
         let witnesses = report.non_exhaustiveness_witnesses;
         if !witnesses.is_empty() {
             self.diagnostics.push(BodyValidationDiagnostic::MissingMatchArms {
-                match_expr: id,
-                uncovered_patterns: missing_match_arms(&cx, match_expr_ty, witnesses, arms),
+                match_expr,
+                uncovered_patterns: missing_match_arms(&cx, scrut_ty, witnesses, arms),
             });
         }
     }
@@ -379,7 +378,7 @@ fn missing_match_arms<'p>(
     arms: &[MatchArm],
 ) -> String {
     struct DisplayWitness<'a, 'p>(&'a DeconstructedPat<'p>, &'a MatchCheckCtx<'a, 'p>);
-    impl<'a, 'p> fmt::Display for DisplayWitness<'a, 'p> {
+    impl fmt::Display for DisplayWitness<'_, '_> {
         fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
             let DisplayWitness(witness, cx) = *self;
             let pat = witness.to_pat(cx);
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs
index 8b0f051b46b..859a37804ae 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs
@@ -146,8 +146,9 @@ impl<'a> PatCtxt<'a> {
                 PatKind::Leaf { subpatterns }
             }
 
-            hir_def::expr::Pat::Bind { ref name, subpat, .. } => {
+            hir_def::expr::Pat::Bind { id, subpat, .. } => {
                 let bm = self.infer.pat_binding_modes[&pat];
+                let name = &self.body.bindings[id].name;
                 match (bm, ty.kind(Interner)) {
                     (BindingMode::Ref(_), TyKind::Ref(.., rty)) => ty = rty,
                     (BindingMode::Ref(_), _) => {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs
index 431ab949b46..d25c0ccf00d 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs
@@ -94,8 +94,10 @@ fn walk_unsafe(
                 unsafe_expr_cb(UnsafeExpr { expr: current, inside_unsafe_block });
             }
         }
-        Expr::Unsafe { body: child } => {
-            return walk_unsafe(db, infer, def, body, *child, true, unsafe_expr_cb);
+        Expr::Unsafe { .. } => {
+            return expr.walk_child_exprs(|child| {
+                walk_unsafe(db, infer, def, body, child, true, unsafe_expr_cb);
+            });
         }
         _ => {}
     }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs
index b22064d8c42..bd3eccfe43d 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs
@@ -5,8 +5,9 @@
 use std::fmt::{self, Debug};
 
 use base_db::CrateId;
-use chalk_ir::BoundVar;
+use chalk_ir::{BoundVar, TyKind};
 use hir_def::{
+    adt::VariantData,
     body,
     db::DefDatabase,
     find_path,
@@ -14,9 +15,9 @@ use hir_def::{
     item_scope::ItemInNs,
     lang_item::{LangItem, LangItemTarget},
     path::{Path, PathKind},
-    type_ref::{ConstScalar, TraitBoundModifier, TypeBound, TypeRef},
+    type_ref::{TraitBoundModifier, TypeBound, TypeRef},
     visibility::Visibility,
-    HasModule, ItemContainerId, Lookup, ModuleDefId, ModuleId, TraitId,
+    HasModule, ItemContainerId, LocalFieldId, Lookup, ModuleDefId, ModuleId, TraitId,
 };
 use hir_expand::{hygiene::Hygiene, name::Name};
 use intern::{Internable, Interned};
@@ -25,14 +26,17 @@ use smallvec::SmallVec;
 
 use crate::{
     db::HirDatabase,
-    from_assoc_type_id, from_foreign_def_id, from_placeholder_idx, lt_from_placeholder_idx,
+    from_assoc_type_id, from_foreign_def_id, from_placeholder_idx,
+    layout::layout_of_ty,
+    lt_from_placeholder_idx,
     mapping::from_chalk,
+    mir::pad16,
     primitive, to_assoc_type_id,
     utils::{self, generics},
-    AdtId, AliasEq, AliasTy, Binders, CallableDefId, CallableSig, Const, ConstValue, DomainGoal,
-    GenericArg, ImplTraitId, Interner, Lifetime, LifetimeData, LifetimeOutlives, Mutability,
-    OpaqueTy, ProjectionTy, ProjectionTyExt, QuantifiedWhereClause, Scalar, Substitution, TraitRef,
-    TraitRefExt, Ty, TyExt, TyKind, WhereClause,
+    AdtId, AliasEq, AliasTy, Binders, CallableDefId, CallableSig, Const, ConstScalar, ConstValue,
+    DomainGoal, GenericArg, ImplTraitId, Interner, Lifetime, LifetimeData, LifetimeOutlives,
+    MemoryMap, Mutability, OpaqueTy, ProjectionTy, ProjectionTyExt, QuantifiedWhereClause, Scalar,
+    Substitution, TraitRef, TraitRefExt, Ty, TyExt, WhereClause,
 };
 
 pub trait HirWrite: fmt::Write {
@@ -362,20 +366,176 @@ impl HirDisplay for GenericArg {
 impl HirDisplay for Const {
     fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
         let data = self.interned();
-        match data.value {
+        match &data.value {
             ConstValue::BoundVar(idx) => idx.hir_fmt(f),
             ConstValue::InferenceVar(..) => write!(f, "#c#"),
             ConstValue::Placeholder(idx) => {
-                let id = from_placeholder_idx(f.db, idx);
+                let id = from_placeholder_idx(f.db, *idx);
                 let generics = generics(f.db.upcast(), id.parent);
                 let param_data = &generics.params.type_or_consts[id.local_id];
                 write!(f, "{}", param_data.name().unwrap())
             }
-            ConstValue::Concrete(c) => write!(f, "{}", c.interned),
+            ConstValue::Concrete(c) => match &c.interned {
+                ConstScalar::Bytes(b, m) => render_const_scalar(f, &b, m, &data.ty),
+                ConstScalar::Unknown => f.write_char('_'),
+            },
         }
     }
 }
 
+pub struct HexifiedConst(pub Const);
+
+impl HirDisplay for HexifiedConst {
+    fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
+        let data = &self.0.data(Interner);
+        if let TyKind::Scalar(s) = data.ty.kind(Interner) {
+            if matches!(s, Scalar::Int(_) | Scalar::Uint(_)) {
+                if let ConstValue::Concrete(c) = &data.value {
+                    if let ConstScalar::Bytes(b, m) = &c.interned {
+                        let value = u128::from_le_bytes(pad16(b, false));
+                        if value >= 10 {
+                            render_const_scalar(f, &b, m, &data.ty)?;
+                            return write!(f, " ({:#X})", value);
+                        }
+                    }
+                }
+            }
+        }
+        self.0.hir_fmt(f)
+    }
+}
+
+fn render_const_scalar(
+    f: &mut HirFormatter<'_>,
+    b: &[u8],
+    memory_map: &MemoryMap,
+    ty: &Ty,
+) -> Result<(), HirDisplayError> {
+    match ty.kind(Interner) {
+        chalk_ir::TyKind::Scalar(s) => match s {
+            Scalar::Bool => write!(f, "{}", if b[0] == 0 { false } else { true }),
+            Scalar::Char => {
+                let x = u128::from_le_bytes(pad16(b, false)) as u32;
+                let Ok(c) = char::try_from(x) else {
+                    return f.write_str("<unicode-error>");
+                };
+                write!(f, "{c:?}")
+            }
+            Scalar::Int(_) => {
+                let x = i128::from_le_bytes(pad16(b, true));
+                write!(f, "{x}")
+            }
+            Scalar::Uint(_) => {
+                let x = u128::from_le_bytes(pad16(b, false));
+                write!(f, "{x}")
+            }
+            Scalar::Float(fl) => match fl {
+                chalk_ir::FloatTy::F32 => {
+                    let x = f32::from_le_bytes(b.try_into().unwrap());
+                    write!(f, "{x:?}")
+                }
+                chalk_ir::FloatTy::F64 => {
+                    let x = f64::from_le_bytes(b.try_into().unwrap());
+                    write!(f, "{x:?}")
+                }
+            },
+        },
+        chalk_ir::TyKind::Ref(_, _, t) => match t.kind(Interner) {
+            chalk_ir::TyKind::Str => {
+                let addr = usize::from_le_bytes(b[0..b.len() / 2].try_into().unwrap());
+                let bytes = memory_map.0.get(&addr).map(|x| &**x).unwrap_or(&[]);
+                let s = std::str::from_utf8(bytes).unwrap_or("<utf8-error>");
+                write!(f, "{s:?}")
+            }
+            _ => f.write_str("<ref-not-supported>"),
+        },
+        chalk_ir::TyKind::Tuple(_, subst) => {
+            // FIXME: Remove this line. If the target data layout is independent
+            // of the krate, the `db.target_data_layout` and its callers like `layout_of_ty` don't need
+            // to get krate. Otherwise, we need to get krate from the final callers of the hir display
+            // infrastructure and have it here as a field on `f`.
+            let krate = *f.db.crate_graph().crates_in_topological_order().last().unwrap();
+            let Ok(layout) = layout_of_ty(f.db, ty, krate) else {
+                return f.write_str("<layout-error>");
+            };
+            f.write_str("(")?;
+            let mut first = true;
+            for (id, ty) in subst.iter(Interner).enumerate() {
+                if first {
+                    first = false;
+                } else {
+                    f.write_str(", ")?;
+                }
+                let ty = ty.assert_ty_ref(Interner); // Tuple only has type argument
+                let offset = layout.fields.offset(id).bytes_usize();
+                let Ok(layout) = layout_of_ty(f.db, &ty, krate) else {
+                    f.write_str("<layout-error>")?;
+                    continue;
+                };
+                let size = layout.size.bytes_usize();
+                render_const_scalar(f, &b[offset..offset + size], memory_map, &ty)?;
+            }
+            f.write_str(")")
+        }
+        chalk_ir::TyKind::Adt(adt, subst) => match adt.0 {
+            hir_def::AdtId::StructId(s) => {
+                let data = f.db.struct_data(s);
+                let Ok(layout) = f.db.layout_of_adt(adt.0, subst.clone()) else {
+                    return f.write_str("<layout-error>");
+                };
+                match data.variant_data.as_ref() {
+                    VariantData::Record(fields) | VariantData::Tuple(fields) => {
+                        let field_types = f.db.field_types(s.into());
+                        let krate = adt.0.module(f.db.upcast()).krate();
+                        let render_field = |f: &mut HirFormatter<'_>, id: LocalFieldId| {
+                            let offset = layout
+                                .fields
+                                .offset(u32::from(id.into_raw()) as usize)
+                                .bytes_usize();
+                            let ty = field_types[id].clone().substitute(Interner, subst);
+                            let Ok(layout) = layout_of_ty(f.db, &ty, krate) else {
+                                return f.write_str("<layout-error>");
+                            };
+                            let size = layout.size.bytes_usize();
+                            render_const_scalar(f, &b[offset..offset + size], memory_map, &ty)
+                        };
+                        let mut it = fields.iter();
+                        if matches!(data.variant_data.as_ref(), VariantData::Record(_)) {
+                            write!(f, "{} {{", data.name)?;
+                            if let Some((id, data)) = it.next() {
+                                write!(f, " {}: ", data.name)?;
+                                render_field(f, id)?;
+                            }
+                            for (id, data) in it {
+                                write!(f, ", {}: ", data.name)?;
+                                render_field(f, id)?;
+                            }
+                            write!(f, " }}")?;
+                        } else {
+                            let mut it = it.map(|x| x.0);
+                            write!(f, "{}(", data.name)?;
+                            if let Some(id) = it.next() {
+                                render_field(f, id)?;
+                            }
+                            for id in it {
+                                write!(f, ", ")?;
+                                render_field(f, id)?;
+                            }
+                            write!(f, ")")?;
+                        }
+                        return Ok(());
+                    }
+                    VariantData::Unit => write!(f, "{}", data.name),
+                }
+            }
+            hir_def::AdtId::UnionId(u) => write!(f, "{}", f.db.union_data(u).name),
+            hir_def::AdtId::EnumId(_) => f.write_str("<enum-not-supported>"),
+        },
+        chalk_ir::TyKind::FnDef(..) => ty.hir_fmt(f),
+        _ => f.write_str("<not-supported>"),
+    }
+}
+
 impl HirDisplay for BoundVar {
     fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
         write!(f, "?{}.{}", self.debruijn.depth(), self.index)
@@ -614,8 +774,9 @@ impl HirDisplay for Ty {
                                     {
                                         return true;
                                     }
-                                    if let Some(ConstValue::Concrete(c)) =
-                                        parameter.constant(Interner).map(|x| x.data(Interner).value)
+                                    if let Some(ConstValue::Concrete(c)) = parameter
+                                        .constant(Interner)
+                                        .map(|x| &x.data(Interner).value)
                                     {
                                         if c.interned == ConstScalar::Unknown {
                                             return true;
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
index 767afdf9eb4..7de5b4295fc 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
@@ -17,11 +17,12 @@ use std::ops::Index;
 use std::sync::Arc;
 
 use chalk_ir::{cast::Cast, ConstValue, DebruijnIndex, Mutability, Safety, Scalar, TypeFlags};
+use either::Either;
 use hir_def::{
     body::Body,
     builtin_type::{BuiltinInt, BuiltinType, BuiltinUint},
     data::{ConstData, StaticData},
-    expr::{BindingAnnotation, ExprId, ExprOrPatId, PatId},
+    expr::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, PatId},
     lang_item::{LangItem, LangItemTarget},
     layout::Integer,
     path::Path,
@@ -30,10 +31,9 @@ use hir_def::{
     AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, FunctionId, HasModule,
     ItemContainerId, Lookup, TraitId, TypeAliasId, VariantId,
 };
-use hir_expand::name::name;
-use itertools::Either;
+use hir_expand::name::{name, Name};
 use la_arena::ArenaMap;
-use rustc_hash::FxHashMap;
+use rustc_hash::{FxHashMap, FxHashSet};
 use stdx::always;
 
 use crate::{
@@ -66,8 +66,10 @@ pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<Infer
     let mut ctx = InferenceContext::new(db, def, &body, resolver);
 
     match def {
+        DefWithBodyId::FunctionId(f) => {
+            ctx.collect_fn(f);
+        }
         DefWithBodyId::ConstId(c) => ctx.collect_const(&db.const_data(c)),
-        DefWithBodyId::FunctionId(f) => ctx.collect_fn(f),
         DefWithBodyId::StaticId(s) => ctx.collect_static(&db.static_data(s)),
         DefWithBodyId::VariantId(v) => {
             ctx.return_ty = TyBuilder::builtin(match db.enum_data(v.parent).variant_body_type() {
@@ -144,44 +146,6 @@ impl Default for BindingMode {
     }
 }
 
-/// Used to generalize patterns and assignee expressions.
-trait PatLike: Into<ExprOrPatId> + Copy {
-    type BindingMode: Copy;
-
-    fn infer(
-        this: &mut InferenceContext<'_>,
-        id: Self,
-        expected_ty: &Ty,
-        default_bm: Self::BindingMode,
-    ) -> Ty;
-}
-
-impl PatLike for ExprId {
-    type BindingMode = ();
-
-    fn infer(
-        this: &mut InferenceContext<'_>,
-        id: Self,
-        expected_ty: &Ty,
-        _: Self::BindingMode,
-    ) -> Ty {
-        this.infer_assignee_expr(id, expected_ty)
-    }
-}
-
-impl PatLike for PatId {
-    type BindingMode = BindingMode;
-
-    fn infer(
-        this: &mut InferenceContext<'_>,
-        id: Self,
-        expected_ty: &Ty,
-        default_bm: Self::BindingMode,
-    ) -> Ty {
-        this.infer_pat(id, expected_ty, default_bm)
-    }
-}
-
 #[derive(Debug)]
 pub(crate) struct InferOk<T> {
     value: T,
@@ -200,11 +164,45 @@ pub(crate) type InferResult<T> = Result<InferOk<T>, TypeError>;
 
 #[derive(Debug, PartialEq, Eq, Clone)]
 pub enum InferenceDiagnostic {
-    NoSuchField { expr: ExprId },
-    PrivateField { expr: ExprId, field: FieldId },
-    PrivateAssocItem { id: ExprOrPatId, item: AssocItemId },
-    BreakOutsideOfLoop { expr: ExprId, is_break: bool },
-    MismatchedArgCount { call_expr: ExprId, expected: usize, found: usize },
+    NoSuchField {
+        expr: ExprId,
+    },
+    PrivateField {
+        expr: ExprId,
+        field: FieldId,
+    },
+    PrivateAssocItem {
+        id: ExprOrPatId,
+        item: AssocItemId,
+    },
+    UnresolvedField {
+        expr: ExprId,
+        receiver: Ty,
+        name: Name,
+        method_with_same_name_exists: bool,
+    },
+    UnresolvedMethodCall {
+        expr: ExprId,
+        receiver: Ty,
+        name: Name,
+        /// Contains the type the field resolves to
+        field_with_same_name: Option<Ty>,
+    },
+    // FIXME: Make this proper
+    BreakOutsideOfLoop {
+        expr: ExprId,
+        is_break: bool,
+        bad_value_break: bool,
+    },
+    MismatchedArgCount {
+        call_expr: ExprId,
+        expected: usize,
+        found: usize,
+    },
+    ExpectedFunction {
+        call_expr: ExprId,
+        found: Ty,
+    },
 }
 
 /// A mismatch between an expected and an inferred type.
@@ -293,8 +291,10 @@ pub enum Adjust {
 /// call, with the signature `&'a T -> &'a U` or `&'a mut T -> &'a mut U`.
 /// The target type is `U` in both cases, with the region and mutability
 /// being those shared by both the receiver and the returned reference.
+///
+/// Mutability is `None` when we are not sure.
 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
-pub struct OverloadedDeref(pub Mutability);
+pub struct OverloadedDeref(pub Option<Mutability>);
 
 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
 pub enum AutoBorrow {
@@ -354,7 +354,10 @@ pub struct InferenceResult {
     /// **Note**: When a pattern type is resolved it may still contain
     /// unresolved or missing subpatterns or subpatterns of mismatched types.
     pub type_of_pat: ArenaMap<PatId, Ty>,
+    pub type_of_binding: ArenaMap<BindingId, Ty>,
     pub type_of_rpit: ArenaMap<RpitId, Ty>,
+    /// Type of the result of `.into_iter()` on the for. `ExprId` is the one of the whole for loop.
+    pub type_of_for_iterator: FxHashMap<ExprId, Ty>,
     type_mismatches: FxHashMap<ExprOrPatId, TypeMismatch>,
     /// Interned common types to return references to.
     standard_types: InternedStandardTypes,
@@ -389,18 +392,15 @@ impl InferenceResult {
     pub fn type_mismatch_for_pat(&self, pat: PatId) -> Option<&TypeMismatch> {
         self.type_mismatches.get(&pat.into())
     }
+    pub fn type_mismatches(&self) -> impl Iterator<Item = (ExprOrPatId, &TypeMismatch)> {
+        self.type_mismatches.iter().map(|(expr_or_pat, mismatch)| (*expr_or_pat, mismatch))
+    }
     pub fn expr_type_mismatches(&self) -> impl Iterator<Item = (ExprId, &TypeMismatch)> {
         self.type_mismatches.iter().filter_map(|(expr_or_pat, mismatch)| match *expr_or_pat {
             ExprOrPatId::ExprId(expr) => Some((expr, mismatch)),
             _ => None,
         })
     }
-    pub fn pat_type_mismatches(&self) -> impl Iterator<Item = (PatId, &TypeMismatch)> {
-        self.type_mismatches.iter().filter_map(|(expr_or_pat, mismatch)| match *expr_or_pat {
-            ExprOrPatId::PatId(pat) => Some((pat, mismatch)),
-            _ => None,
-        })
-    }
 }
 
 impl Index<ExprId> for InferenceResult {
@@ -419,6 +419,14 @@ impl Index<PatId> for InferenceResult {
     }
 }
 
+impl Index<BindingId> for InferenceResult {
+    type Output = Ty;
+
+    fn index(&self, b: BindingId) -> &Ty {
+        self.type_of_binding.get(b).unwrap_or(&self.standard_types.unknown)
+    }
+}
+
 /// The inference context contains all information needed during type inference.
 #[derive(Clone, Debug)]
 pub(crate) struct InferenceContext<'a> {
@@ -428,14 +436,19 @@ pub(crate) struct InferenceContext<'a> {
     pub(crate) resolver: Resolver,
     table: unify::InferenceTable<'a>,
     trait_env: Arc<TraitEnvironment>,
+    /// The traits in scope, disregarding block modules. This is used for caching purposes.
+    traits_in_scope: FxHashSet<TraitId>,
     pub(crate) result: InferenceResult,
     /// The return type of the function being inferred, the closure or async block if we're
     /// currently within one.
     ///
     /// We might consider using a nested inference context for checking
-    /// closures, but currently this is the only field that will change there,
-    /// so it doesn't make sense.
+    /// closures so we can swap all shared things out at once.
     return_ty: Ty,
+    /// If `Some`, this stores coercion information for returned
+    /// expressions. If `None`, this is in a context where return is
+    /// inappropriate, such as a const expression.
+    return_coercion: Option<CoerceMany>,
     /// The resume type and the yield type, respectively, of the generator being inferred.
     resume_yield_tys: Option<(Ty, Ty)>,
     diverges: Diverges,
@@ -447,7 +460,7 @@ struct BreakableContext {
     /// Whether this context contains at least one break expression.
     may_break: bool,
     /// The coercion target of the context.
-    coerce: CoerceMany,
+    coerce: Option<CoerceMany>,
     /// The optional label of the context.
     label: Option<name::Name>,
     kind: BreakableKind,
@@ -503,16 +516,22 @@ impl<'a> InferenceContext<'a> {
             trait_env,
             return_ty: TyKind::Error.intern(Interner), // set in collect_* calls
             resume_yield_tys: None,
+            return_coercion: None,
             db,
             owner,
             body,
+            traits_in_scope: resolver.traits_in_scope(db.upcast()),
             resolver,
             diverges: Diverges::Maybe,
             breakables: Vec::new(),
         }
     }
 
-    fn resolve_all(self) -> InferenceResult {
+    // FIXME: This function should be private in module. It is currently only used in the consteval, since we need
+    // `InferenceResult` in the middle of inference. See the fixme comment in `consteval::eval_to_const`. If you
+    // used this function for another workaround, mention it here. If you really need this function and believe that
+    // there is no problem in it being `pub(crate)`, remove this comment.
+    pub(crate) fn resolve_all(self) -> InferenceResult {
         let InferenceContext { mut table, mut result, .. } = self;
 
         table.fallback_if_possible();
@@ -528,13 +547,46 @@ impl<'a> InferenceContext<'a> {
         for ty in result.type_of_pat.values_mut() {
             *ty = table.resolve_completely(ty.clone());
         }
-        for ty in result.type_of_rpit.iter_mut().map(|x| x.1) {
+        for ty in result.type_of_binding.values_mut() {
+            *ty = table.resolve_completely(ty.clone());
+        }
+        for ty in result.type_of_rpit.values_mut() {
+            *ty = table.resolve_completely(ty.clone());
+        }
+        for ty in result.type_of_for_iterator.values_mut() {
             *ty = table.resolve_completely(ty.clone());
         }
         for mismatch in result.type_mismatches.values_mut() {
             mismatch.expected = table.resolve_completely(mismatch.expected.clone());
             mismatch.actual = table.resolve_completely(mismatch.actual.clone());
         }
+        result.diagnostics.retain_mut(|diagnostic| {
+            if let InferenceDiagnostic::ExpectedFunction { found: ty, .. }
+            | InferenceDiagnostic::UnresolvedField { receiver: ty, .. }
+            | InferenceDiagnostic::UnresolvedMethodCall { receiver: ty, .. } = diagnostic
+            {
+                *ty = table.resolve_completely(ty.clone());
+                // FIXME: Remove this when we are on par with rustc in terms of inference
+                if ty.contains_unknown() {
+                    return false;
+                }
+
+                if let InferenceDiagnostic::UnresolvedMethodCall { field_with_same_name, .. } =
+                    diagnostic
+                {
+                    let clear = if let Some(ty) = field_with_same_name {
+                        *ty = table.resolve_completely(ty.clone());
+                        ty.contains_unknown()
+                    } else {
+                        false
+                    };
+                    if clear {
+                        *field_with_same_name = None;
+                    }
+                }
+            }
+            true
+        });
         for (_, subst) in result.method_resolutions.values_mut() {
             *subst = table.resolve_completely(subst.clone());
         }
@@ -580,7 +632,7 @@ impl<'a> InferenceContext<'a> {
             let ty = self.insert_type_vars(ty);
             let ty = self.normalize_associated_types_in(ty);
 
-            self.infer_pat(*pat, &ty, BindingMode::default());
+            self.infer_top_pat(*pat, &ty);
         }
         let error_ty = &TypeRef::Error;
         let return_ty = if data.has_async_kw() {
@@ -632,10 +684,19 @@ impl<'a> InferenceContext<'a> {
         };
 
         self.return_ty = self.normalize_associated_types_in(return_ty);
+        self.return_coercion = Some(CoerceMany::new(self.return_ty.clone()));
     }
 
     fn infer_body(&mut self) {
-        self.infer_expr_coerce(self.body.body_expr, &Expectation::has_type(self.return_ty.clone()));
+        match self.return_coercion {
+            Some(_) => self.infer_return(self.body.body_expr),
+            None => {
+                _ = self.infer_expr_coerce(
+                    self.body.body_expr,
+                    &Expectation::has_type(self.return_ty.clone()),
+                )
+            }
+        }
     }
 
     fn write_expr_ty(&mut self, expr: ExprId, ty: Ty) {
@@ -662,12 +723,15 @@ impl<'a> InferenceContext<'a> {
         self.result.type_of_pat.insert(pat, ty);
     }
 
+    fn write_binding_ty(&mut self, id: BindingId, ty: Ty) {
+        self.result.type_of_binding.insert(id, ty);
+    }
+
     fn push_diagnostic(&mut self, diagnostic: InferenceDiagnostic) {
         self.result.diagnostics.push(diagnostic);
     }
 
     fn make_ty(&mut self, type_ref: &TypeRef) -> Ty {
-        // FIXME use right resolver for block
         let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver);
         let ty = ctx.lower_ty(type_ref);
         let ty = self.insert_type_vars(ty);
@@ -681,11 +745,9 @@ impl<'a> InferenceContext<'a> {
     /// Replaces ConstScalar::Unknown by a new type var, so we can maybe still infer it.
     fn insert_const_vars_shallow(&mut self, c: Const) -> Const {
         let data = c.data(Interner);
-        match data.value {
+        match &data.value {
             ConstValue::Concrete(cc) => match cc.interned {
-                hir_def::type_ref::ConstScalar::Unknown => {
-                    self.table.new_const_var(data.ty.clone())
-                }
+                crate::ConstScalar::Unknown => self.table.new_const_var(data.ty.clone()),
                 _ => c,
             },
             _ => c,
@@ -785,12 +847,11 @@ impl<'a> InferenceContext<'a> {
             Some(path) => path,
             None => return (self.err_ty(), None),
         };
-        let resolver = &self.resolver;
         let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver);
         // FIXME: this should resolve assoc items as well, see this example:
         // https://play.rust-lang.org/?gist=087992e9e22495446c01c0d4e2d69521
         let (resolution, unresolved) = if value_ns {
-            match resolver.resolve_path_in_value_ns(self.db.upcast(), path.mod_path()) {
+            match self.resolver.resolve_path_in_value_ns(self.db.upcast(), path.mod_path()) {
                 Some(ResolveValueResult::ValueNs(value)) => match value {
                     ValueNs::EnumVariantId(var) => {
                         let substs = ctx.substs_from_path(path, var.into(), true);
@@ -811,7 +872,7 @@ impl<'a> InferenceContext<'a> {
                 None => return (self.err_ty(), None),
             }
         } else {
-            match resolver.resolve_path_in_type_ns(self.db.upcast(), path.mod_path()) {
+            match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path.mod_path()) {
                 Some(it) => it,
                 None => return (self.err_ty(), None),
             }
@@ -866,7 +927,10 @@ impl<'a> InferenceContext<'a> {
                 // FIXME potentially resolve assoc type
                 (self.err_ty(), None)
             }
-            TypeNs::AdtId(AdtId::EnumId(_)) | TypeNs::BuiltinType(_) | TypeNs::TraitId(_) => {
+            TypeNs::AdtId(AdtId::EnumId(_))
+            | TypeNs::BuiltinType(_)
+            | TypeNs::TraitId(_)
+            | TypeNs::TraitAliasId(_) => {
                 // FIXME diagnostic
                 (self.err_ty(), None)
             }
@@ -1018,6 +1082,15 @@ impl<'a> InferenceContext<'a> {
         let struct_ = self.resolve_lang_item(LangItem::VaList)?.as_struct()?;
         Some(struct_.into())
     }
+
+    fn get_traits_in_scope(&self) -> Either<FxHashSet<TraitId>, &FxHashSet<TraitId>> {
+        let mut b_traits = self.resolver.traits_in_scope_from_block_scopes().peekable();
+        if b_traits.peek().is_some() {
+            Either::Left(self.traits_in_scope.iter().copied().chain(b_traits).collect())
+        } else {
+            Either::Right(&self.traits_in_scope)
+        }
+    }
 }
 
 /// When inferring an expression, we propagate downward whatever type hint we
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs
index 3293534a068..48c91530266 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs
@@ -50,11 +50,44 @@ fn success(
 #[derive(Clone, Debug)]
 pub(super) struct CoerceMany {
     expected_ty: Ty,
+    final_ty: Option<Ty>,
 }
 
 impl CoerceMany {
     pub(super) fn new(expected: Ty) -> Self {
-        CoerceMany { expected_ty: expected }
+        CoerceMany { expected_ty: expected, final_ty: None }
+    }
+
+    /// Returns the "expected type" with which this coercion was
+    /// constructed. This represents the "downward propagated" type
+    /// that was given to us at the start of typing whatever construct
+    /// we are typing (e.g., the match expression).
+    ///
+    /// Typically, this is used as the expected type when
+    /// type-checking each of the alternative expressions whose types
+    /// we are trying to merge.
+    pub(super) fn expected_ty(&self) -> Ty {
+        self.expected_ty.clone()
+    }
+
+    /// Returns the current "merged type", representing our best-guess
+    /// at the LUB of the expressions we've seen so far (if any). This
+    /// isn't *final* until you call `self.complete()`, which will return
+    /// the merged type.
+    pub(super) fn merged_ty(&self) -> Ty {
+        self.final_ty.clone().unwrap_or_else(|| self.expected_ty.clone())
+    }
+
+    pub(super) fn complete(self, ctx: &mut InferenceContext<'_>) -> Ty {
+        if let Some(final_ty) = self.final_ty {
+            final_ty
+        } else {
+            ctx.result.standard_types.never.clone()
+        }
+    }
+
+    pub(super) fn coerce_forced_unit(&mut self, ctx: &mut InferenceContext<'_>) {
+        self.coerce(ctx, None, &ctx.result.standard_types.unit.clone())
     }
 
     /// Merge two types from different branches, with possible coercion.
@@ -76,25 +109,25 @@ impl CoerceMany {
         // Special case: two function types. Try to coerce both to
         // pointers to have a chance at getting a match. See
         // https://github.com/rust-lang/rust/blob/7b805396bf46dce972692a6846ce2ad8481c5f85/src/librustc_typeck/check/coercion.rs#L877-L916
-        let sig = match (self.expected_ty.kind(Interner), expr_ty.kind(Interner)) {
+        let sig = match (self.merged_ty().kind(Interner), expr_ty.kind(Interner)) {
             (TyKind::FnDef(..) | TyKind::Closure(..), TyKind::FnDef(..) | TyKind::Closure(..)) => {
                 // FIXME: we're ignoring safety here. To be more correct, if we have one FnDef and one Closure,
                 // we should be coercing the closure to a fn pointer of the safety of the FnDef
                 cov_mark::hit!(coerce_fn_reification);
                 let sig =
-                    self.expected_ty.callable_sig(ctx.db).expect("FnDef without callable sig");
+                    self.merged_ty().callable_sig(ctx.db).expect("FnDef without callable sig");
                 Some(sig)
             }
             _ => None,
         };
         if let Some(sig) = sig {
             let target_ty = TyKind::Function(sig.to_fn_ptr()).intern(Interner);
-            let result1 = ctx.table.coerce_inner(self.expected_ty.clone(), &target_ty);
+            let result1 = ctx.table.coerce_inner(self.merged_ty(), &target_ty);
             let result2 = ctx.table.coerce_inner(expr_ty.clone(), &target_ty);
             if let (Ok(result1), Ok(result2)) = (result1, result2) {
                 ctx.table.register_infer_ok(result1);
                 ctx.table.register_infer_ok(result2);
-                return self.expected_ty = target_ty;
+                return self.final_ty = Some(target_ty);
             }
         }
 
@@ -102,25 +135,20 @@ impl CoerceMany {
         // type is a type variable and the new one is `!`, trying it the other
         // way around first would mean we make the type variable `!`, instead of
         // just marking it as possibly diverging.
-        if ctx.coerce(expr, &expr_ty, &self.expected_ty).is_ok() {
-            /* self.expected_ty is already correct */
-        } else if ctx.coerce(expr, &self.expected_ty, &expr_ty).is_ok() {
-            self.expected_ty = expr_ty;
+        if let Ok(res) = ctx.coerce(expr, &expr_ty, &self.merged_ty()) {
+            self.final_ty = Some(res);
+        } else if let Ok(res) = ctx.coerce(expr, &self.merged_ty(), &expr_ty) {
+            self.final_ty = Some(res);
         } else {
             if let Some(id) = expr {
                 ctx.result.type_mismatches.insert(
                     id.into(),
-                    TypeMismatch { expected: self.expected_ty.clone(), actual: expr_ty },
+                    TypeMismatch { expected: self.merged_ty().clone(), actual: expr_ty.clone() },
                 );
             }
             cov_mark::hit!(coerce_merge_fail_fallback);
-            /* self.expected_ty is already correct */
         }
     }
-
-    pub(super) fn complete(self) -> Ty {
-        self.expected_ty
-    }
 }
 
 pub fn could_coerce(
@@ -665,7 +693,7 @@ pub(super) fn auto_deref_adjust_steps(autoderef: &Autoderef<'_, '_>) -> Vec<Adju
         .iter()
         .map(|(kind, _source)| match kind {
             // We do not know what kind of deref we require at this point yet
-            AutoderefKind::Overloaded => Some(OverloadedDeref(Mutability::Not)),
+            AutoderefKind::Overloaded => Some(OverloadedDeref(None)),
             AutoderefKind::Builtin => None,
         })
         .zip(targets)
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs
index 175fded8cca..535189ff028 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs
@@ -15,7 +15,6 @@ use hir_def::{
     generics::TypeOrConstParamData,
     lang_item::LangItem,
     path::{GenericArg, GenericArgs},
-    resolver::resolver_for_expr,
     ConstParamId, FieldId, ItemContainerId, Lookup,
 };
 use hir_expand::name::{name, Name};
@@ -25,7 +24,9 @@ use syntax::ast::RangeOp;
 use crate::{
     autoderef::{self, Autoderef},
     consteval,
-    infer::{coerce::CoerceMany, find_continuable, BreakableKind},
+    infer::{
+        coerce::CoerceMany, find_continuable, pat::contains_explicit_ref_binding, BreakableKind,
+    },
     lower::{
         const_or_path_to_chalk, generic_arg_to_chalk, lower_to_chalk_mutability, ParamLoweringMode,
     },
@@ -39,8 +40,8 @@ use crate::{
 };
 
 use super::{
-    coerce::auto_deref_adjust_steps, find_breakable, BindingMode, BreakableContext, Diverges,
-    Expectation, InferenceContext, InferenceDiagnostic, TypeMismatch,
+    coerce::auto_deref_adjust_steps, find_breakable, BreakableContext, Diverges, Expectation,
+    InferenceContext, InferenceDiagnostic, TypeMismatch,
 };
 
 impl<'a> InferenceContext<'a> {
@@ -58,6 +59,10 @@ impl<'a> InferenceContext<'a> {
         ty
     }
 
+    pub(crate) fn infer_expr_no_expect(&mut self, tgt_expr: ExprId) -> Ty {
+        self.infer_expr_inner(tgt_expr, &Expectation::None)
+    }
+
     /// Infer type of expression with possibly implicit coerce to the expected type.
     /// Return the type after possible coercion.
     pub(super) fn infer_expr_coerce(&mut self, expr: ExprId, expected: &Expectation) -> Ty {
@@ -78,6 +83,30 @@ impl<'a> InferenceContext<'a> {
         }
     }
 
+    pub(super) fn infer_expr_coerce_never(&mut self, expr: ExprId, expected: &Expectation) -> Ty {
+        let ty = self.infer_expr_inner(expr, expected);
+        // While we don't allow *arbitrary* coercions here, we *do* allow
+        // coercions from ! to `expected`.
+        if ty.is_never() {
+            if let Some(adjustments) = self.result.expr_adjustments.get(&expr) {
+                return if let [Adjustment { kind: Adjust::NeverToAny, target }] = &**adjustments {
+                    target.clone()
+                } else {
+                    self.err_ty()
+                };
+            }
+
+            let adj_ty = self.table.new_type_var();
+            self.write_expr_adj(
+                expr,
+                vec![Adjustment { kind: Adjust::NeverToAny, target: adj_ty.clone() }],
+            );
+            adj_ty
+        } else {
+            ty
+        }
+    }
+
     fn infer_expr_inner(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty {
         self.db.unwind_if_cancelled();
 
@@ -85,7 +114,7 @@ impl<'a> InferenceContext<'a> {
             Expr::Missing => self.err_ty(),
             &Expr::If { condition, then_branch, else_branch } => {
                 let expected = &expected.adjust_for_branches(&mut self.table);
-                self.infer_expr(
+                self.infer_expr_coerce_never(
                     condition,
                     &Expectation::HasType(self.result.standard_types.bool_.clone()),
                 );
@@ -97,59 +126,39 @@ impl<'a> InferenceContext<'a> {
                 both_arms_diverge &= mem::replace(&mut self.diverges, Diverges::Maybe);
                 let mut coerce = CoerceMany::new(expected.coercion_target_type(&mut self.table));
                 coerce.coerce(self, Some(then_branch), &then_ty);
-                let else_ty = match else_branch {
-                    Some(else_branch) => self.infer_expr_inner(else_branch, expected),
-                    None => TyBuilder::unit(),
-                };
+                match else_branch {
+                    Some(else_branch) => {
+                        let else_ty = self.infer_expr_inner(else_branch, expected);
+                        coerce.coerce(self, Some(else_branch), &else_ty);
+                    }
+                    None => {
+                        coerce.coerce_forced_unit(self);
+                    }
+                }
                 both_arms_diverge &= self.diverges;
-                // FIXME: create a synthetic `else {}` so we have something to refer to here instead of None?
-                coerce.coerce(self, else_branch, &else_ty);
 
                 self.diverges = condition_diverges | both_arms_diverge;
 
-                coerce.complete()
+                coerce.complete(self)
             }
             &Expr::Let { pat, expr } => {
                 let input_ty = self.infer_expr(expr, &Expectation::none());
-                self.infer_pat(pat, &input_ty, BindingMode::default());
+                self.infer_top_pat(pat, &input_ty);
                 self.result.standard_types.bool_.clone()
             }
             Expr::Block { statements, tail, label, id: _ } => {
-                let old_resolver = mem::replace(
-                    &mut self.resolver,
-                    resolver_for_expr(self.db.upcast(), self.owner, tgt_expr),
-                );
-                let ty = match label {
-                    Some(_) => {
-                        let break_ty = self.table.new_type_var();
-                        let (breaks, ty) = self.with_breakable_ctx(
-                            BreakableKind::Block,
-                            break_ty.clone(),
-                            *label,
-                            |this| {
-                                this.infer_block(
-                                    tgt_expr,
-                                    statements,
-                                    *tail,
-                                    &Expectation::has_type(break_ty),
-                                )
-                            },
-                        );
-                        breaks.unwrap_or(ty)
-                    }
-                    None => self.infer_block(tgt_expr, statements, *tail, expected),
-                };
-                self.resolver = old_resolver;
-                ty
+                self.infer_block(tgt_expr, statements, *tail, *label, expected)
+            }
+            Expr::Unsafe { id: _, statements, tail } => {
+                self.infer_block(tgt_expr, statements, *tail, None, expected)
             }
-            Expr::Unsafe { body } => self.infer_expr(*body, expected),
-            Expr::Const { body } => {
-                self.with_breakable_ctx(BreakableKind::Border, self.err_ty(), None, |this| {
-                    this.infer_expr(*body, expected)
+            Expr::Const { id: _, statements, tail } => {
+                self.with_breakable_ctx(BreakableKind::Border, None, None, |this| {
+                    this.infer_block(tgt_expr, statements, *tail, None, expected)
                 })
                 .1
             }
-            Expr::TryBlock { body } => {
+            Expr::TryBlock { id: _, statements, tail } => {
                 // The type that is returned from the try block
                 let try_ty = self.table.new_type_var();
                 if let Some(ty) = expected.only_has_type(&mut self.table) {
@@ -160,28 +169,41 @@ impl<'a> InferenceContext<'a> {
                 let ok_ty =
                     self.resolve_associated_type(try_ty.clone(), self.resolve_ops_try_output());
 
-                self.with_breakable_ctx(BreakableKind::Block, ok_ty.clone(), None, |this| {
-                    this.infer_expr(*body, &Expectation::has_type(ok_ty));
-                });
-
+                self.infer_block(
+                    tgt_expr,
+                    statements,
+                    *tail,
+                    None,
+                    &Expectation::has_type(ok_ty.clone()),
+                );
                 try_ty
             }
-            Expr::Async { body } => {
+            Expr::Async { id: _, statements, tail } => {
                 let ret_ty = self.table.new_type_var();
                 let prev_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
                 let prev_ret_ty = mem::replace(&mut self.return_ty, ret_ty.clone());
+                let prev_ret_coercion =
+                    mem::replace(&mut self.return_coercion, Some(CoerceMany::new(ret_ty.clone())));
 
                 let (_, inner_ty) =
-                    self.with_breakable_ctx(BreakableKind::Border, self.err_ty(), None, |this| {
-                        this.infer_expr_coerce(*body, &Expectation::has_type(ret_ty))
+                    self.with_breakable_ctx(BreakableKind::Border, None, None, |this| {
+                        this.infer_block(
+                            tgt_expr,
+                            statements,
+                            *tail,
+                            None,
+                            &Expectation::has_type(ret_ty),
+                        )
                     });
 
                 self.diverges = prev_diverges;
                 self.return_ty = prev_ret_ty;
+                self.return_coercion = prev_ret_coercion;
 
                 // Use the first type parameter as the output type of future.
                 // existential type AsyncBlockImplTrait<InnerType>: Future<Output = InnerType>
-                let impl_trait_id = crate::ImplTraitId::AsyncBlockTypeImplTrait(self.owner, *body);
+                let impl_trait_id =
+                    crate::ImplTraitId::AsyncBlockTypeImplTrait(self.owner, tgt_expr);
                 let opaque_ty_id = self.db.intern_impl_trait_id(impl_trait_id).into();
                 TyKind::OpaqueType(opaque_ty_id, Substitution::from1(Interner, inner_ty))
                     .intern(Interner)
@@ -191,7 +213,7 @@ impl<'a> InferenceContext<'a> {
                 // let ty = expected.coercion_target_type(&mut self.table);
                 let ty = self.table.new_type_var();
                 let (breaks, ()) =
-                    self.with_breakable_ctx(BreakableKind::Loop, ty, label, |this| {
+                    self.with_breakable_ctx(BreakableKind::Loop, Some(ty), label, |this| {
                         this.infer_expr(body, &Expectation::HasType(TyBuilder::unit()));
                     });
 
@@ -204,7 +226,7 @@ impl<'a> InferenceContext<'a> {
                 }
             }
             &Expr::While { condition, body, label } => {
-                self.with_breakable_ctx(BreakableKind::Loop, self.err_ty(), label, |this| {
+                self.with_breakable_ctx(BreakableKind::Loop, None, label, |this| {
                     this.infer_expr(
                         condition,
                         &Expectation::HasType(this.result.standard_types.bool_.clone()),
@@ -220,11 +242,13 @@ impl<'a> InferenceContext<'a> {
                 let iterable_ty = self.infer_expr(iterable, &Expectation::none());
                 let into_iter_ty =
                     self.resolve_associated_type(iterable_ty, self.resolve_into_iter_item());
-                let pat_ty =
-                    self.resolve_associated_type(into_iter_ty, self.resolve_iterator_item());
+                let pat_ty = self
+                    .resolve_associated_type(into_iter_ty.clone(), self.resolve_iterator_item());
+
+                self.result.type_of_for_iterator.insert(tgt_expr, into_iter_ty);
 
-                self.infer_pat(pat, &pat_ty, BindingMode::default());
-                self.with_breakable_ctx(BreakableKind::Loop, self.err_ty(), label, |this| {
+                self.infer_top_pat(pat, &pat_ty);
+                self.with_breakable_ctx(BreakableKind::Loop, None, label, |this| {
                     this.infer_expr(body, &Expectation::HasType(TyBuilder::unit()));
                 });
 
@@ -298,20 +322,24 @@ impl<'a> InferenceContext<'a> {
 
                 // Now go through the argument patterns
                 for (arg_pat, arg_ty) in args.iter().zip(sig_tys) {
-                    self.infer_pat(*arg_pat, &arg_ty, BindingMode::default());
+                    self.infer_top_pat(*arg_pat, &arg_ty);
                 }
 
+                // FIXME: lift these out into a struct
                 let prev_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
                 let prev_ret_ty = mem::replace(&mut self.return_ty, ret_ty.clone());
+                let prev_ret_coercion =
+                    mem::replace(&mut self.return_coercion, Some(CoerceMany::new(ret_ty.clone())));
                 let prev_resume_yield_tys =
                     mem::replace(&mut self.resume_yield_tys, resume_yield_tys);
 
-                self.with_breakable_ctx(BreakableKind::Border, self.err_ty(), None, |this| {
-                    this.infer_expr_coerce(*body, &Expectation::has_type(ret_ty));
+                self.with_breakable_ctx(BreakableKind::Border, None, None, |this| {
+                    this.infer_return(*body);
                 });
 
                 self.diverges = prev_diverges;
                 self.return_ty = prev_ret_ty;
+                self.return_coercion = prev_ret_coercion;
                 self.resume_yield_tys = prev_resume_yield_tys;
 
                 ty
@@ -348,7 +376,13 @@ impl<'a> InferenceContext<'a> {
                         }
                         (params, ret_ty)
                     }
-                    None => (Vec::new(), self.err_ty()), // FIXME diagnostic
+                    None => {
+                        self.result.diagnostics.push(InferenceDiagnostic::ExpectedFunction {
+                            call_expr: tgt_expr,
+                            found: callee_ty.clone(),
+                        });
+                        (Vec::new(), self.err_ty())
+                    }
                 };
                 let indices_to_skip = self.check_legacy_const_generics(derefed_callee, args);
                 self.register_obligations_for_call(&callee_ty);
@@ -381,92 +415,109 @@ impl<'a> InferenceContext<'a> {
             Expr::Match { expr, arms } => {
                 let input_ty = self.infer_expr(*expr, &Expectation::none());
 
-                let expected = expected.adjust_for_branches(&mut self.table);
-
-                let result_ty = if arms.is_empty() {
+                if arms.is_empty() {
+                    self.diverges = Diverges::Always;
                     self.result.standard_types.never.clone()
                 } else {
-                    expected.coercion_target_type(&mut self.table)
-                };
-                let mut coerce = CoerceMany::new(result_ty);
-
-                let matchee_diverges = self.diverges;
-                let mut all_arms_diverge = Diverges::Always;
-
-                for arm in arms.iter() {
-                    self.diverges = Diverges::Maybe;
-                    let _pat_ty = self.infer_pat(arm.pat, &input_ty, BindingMode::default());
-                    if let Some(guard_expr) = arm.guard {
-                        self.infer_expr(
-                            guard_expr,
-                            &Expectation::HasType(self.result.standard_types.bool_.clone()),
-                        );
+                    let matchee_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
+                    let mut all_arms_diverge = Diverges::Always;
+                    for arm in arms.iter() {
+                        let input_ty = self.resolve_ty_shallow(&input_ty);
+                        self.infer_top_pat(arm.pat, &input_ty);
                     }
 
-                    let arm_ty = self.infer_expr_inner(arm.expr, &expected);
-                    all_arms_diverge &= self.diverges;
-                    coerce.coerce(self, Some(arm.expr), &arm_ty);
-                }
+                    let expected = expected.adjust_for_branches(&mut self.table);
+                    let result_ty = match &expected {
+                        // We don't coerce to `()` so that if the match expression is a
+                        // statement it's branches can have any consistent type.
+                        Expectation::HasType(ty) if *ty != self.result.standard_types.unit => {
+                            ty.clone()
+                        }
+                        _ => self.table.new_type_var(),
+                    };
+                    let mut coerce = CoerceMany::new(result_ty);
+
+                    for arm in arms.iter() {
+                        if let Some(guard_expr) = arm.guard {
+                            self.diverges = Diverges::Maybe;
+                            self.infer_expr_coerce_never(
+                                guard_expr,
+                                &Expectation::HasType(self.result.standard_types.bool_.clone()),
+                            );
+                        }
+                        self.diverges = Diverges::Maybe;
+
+                        let arm_ty = self.infer_expr_inner(arm.expr, &expected);
+                        all_arms_diverge &= self.diverges;
+                        coerce.coerce(self, Some(arm.expr), &arm_ty);
+                    }
 
-                self.diverges = matchee_diverges | all_arms_diverge;
+                    self.diverges = matchee_diverges | all_arms_diverge;
 
-                coerce.complete()
+                    coerce.complete(self)
+                }
             }
             Expr::Path(p) => {
-                // FIXME this could be more efficient...
-                let resolver = resolver_for_expr(self.db.upcast(), self.owner, tgt_expr);
-                self.infer_path(&resolver, p, tgt_expr.into()).unwrap_or_else(|| self.err_ty())
+                let g = self.resolver.update_to_inner_scope(self.db.upcast(), self.owner, tgt_expr);
+                let ty = self.infer_path(p, tgt_expr.into()).unwrap_or_else(|| self.err_ty());
+                self.resolver.reset_to_guard(g);
+                ty
             }
             Expr::Continue { label } => {
                 if let None = find_continuable(&mut self.breakables, label.as_ref()) {
                     self.push_diagnostic(InferenceDiagnostic::BreakOutsideOfLoop {
                         expr: tgt_expr,
                         is_break: false,
+                        bad_value_break: false,
                     });
                 };
                 self.result.standard_types.never.clone()
             }
             Expr::Break { expr, label } => {
                 let val_ty = if let Some(expr) = *expr {
-                    self.infer_expr(expr, &Expectation::none())
+                    let opt_coerce_to = match find_breakable(&mut self.breakables, label.as_ref()) {
+                        Some(ctxt) => match &ctxt.coerce {
+                            Some(coerce) => coerce.expected_ty(),
+                            None => {
+                                self.push_diagnostic(InferenceDiagnostic::BreakOutsideOfLoop {
+                                    expr: tgt_expr,
+                                    is_break: true,
+                                    bad_value_break: true,
+                                });
+                                self.err_ty()
+                            }
+                        },
+                        None => self.err_ty(),
+                    };
+                    self.infer_expr_inner(expr, &Expectation::HasType(opt_coerce_to))
                 } else {
                     TyBuilder::unit()
                 };
 
                 match find_breakable(&mut self.breakables, label.as_ref()) {
-                    Some(ctxt) => {
-                        // avoiding the borrowck
-                        let mut coerce = mem::replace(
-                            &mut ctxt.coerce,
-                            CoerceMany::new(expected.coercion_target_type(&mut self.table)),
-                        );
-
-                        // FIXME: create a synthetic `()` during lowering so we have something to refer to here?
-                        coerce.coerce(self, *expr, &val_ty);
-
-                        let ctxt = find_breakable(&mut self.breakables, label.as_ref())
-                            .expect("breakable stack changed during coercion");
-                        ctxt.coerce = coerce;
-                        ctxt.may_break = true;
-                    }
+                    Some(ctxt) => match ctxt.coerce.take() {
+                        Some(mut coerce) => {
+                            coerce.coerce(self, *expr, &val_ty);
+
+                            // Avoiding borrowck
+                            let ctxt = find_breakable(&mut self.breakables, label.as_ref())
+                                .expect("breakable stack changed during coercion");
+                            ctxt.may_break = true;
+                            ctxt.coerce = Some(coerce);
+                        }
+                        None => ctxt.may_break = true,
+                    },
                     None => {
                         self.push_diagnostic(InferenceDiagnostic::BreakOutsideOfLoop {
                             expr: tgt_expr,
                             is_break: true,
+                            bad_value_break: false,
                         });
                     }
                 }
                 self.result.standard_types.never.clone()
             }
-            Expr::Return { expr } => {
-                if let Some(expr) = expr {
-                    self.infer_expr_coerce(*expr, &Expectation::has_type(self.return_ty.clone()));
-                } else {
-                    let unit = TyBuilder::unit();
-                    let _ = self.coerce(Some(tgt_expr), &unit, &self.return_ty.clone());
-                }
-                self.result.standard_types.never.clone()
-            }
+            &Expr::Return { expr } => self.infer_expr_return(expr),
             Expr::Yield { expr } => {
                 if let Some((resume_ty, yield_ty)) = self.resume_yield_tys.clone() {
                     if let Some(expr) = expr {
@@ -483,7 +534,7 @@ impl<'a> InferenceContext<'a> {
             }
             Expr::Yeet { expr } => {
                 if let &Some(expr) = expr {
-                    self.infer_expr_inner(expr, &Expectation::None);
+                    self.infer_expr_no_expect(expr);
                 }
                 self.result.standard_types.never.clone()
             }
@@ -524,71 +575,7 @@ impl<'a> InferenceContext<'a> {
                 }
                 ty
             }
-            Expr::Field { expr, name } => {
-                let receiver_ty = self.infer_expr_inner(*expr, &Expectation::none());
-
-                let mut autoderef = Autoderef::new(&mut self.table, receiver_ty);
-                let mut private_field = None;
-                let ty = autoderef.by_ref().find_map(|(derefed_ty, _)| {
-                    let (field_id, parameters) = match derefed_ty.kind(Interner) {
-                        TyKind::Tuple(_, substs) => {
-                            return name.as_tuple_index().and_then(|idx| {
-                                substs
-                                    .as_slice(Interner)
-                                    .get(idx)
-                                    .map(|a| a.assert_ty_ref(Interner))
-                                    .cloned()
-                            });
-                        }
-                        TyKind::Adt(AdtId(hir_def::AdtId::StructId(s)), parameters) => {
-                            let local_id = self.db.struct_data(*s).variant_data.field(name)?;
-                            let field = FieldId { parent: (*s).into(), local_id };
-                            (field, parameters.clone())
-                        }
-                        TyKind::Adt(AdtId(hir_def::AdtId::UnionId(u)), parameters) => {
-                            let local_id = self.db.union_data(*u).variant_data.field(name)?;
-                            let field = FieldId { parent: (*u).into(), local_id };
-                            (field, parameters.clone())
-                        }
-                        _ => return None,
-                    };
-                    let is_visible = self.db.field_visibilities(field_id.parent)[field_id.local_id]
-                        .is_visible_from(self.db.upcast(), self.resolver.module());
-                    if !is_visible {
-                        if private_field.is_none() {
-                            private_field = Some(field_id);
-                        }
-                        return None;
-                    }
-                    // can't have `write_field_resolution` here because `self.table` is borrowed :(
-                    self.result.field_resolutions.insert(tgt_expr, field_id);
-                    let ty = self.db.field_types(field_id.parent)[field_id.local_id]
-                        .clone()
-                        .substitute(Interner, &parameters);
-                    Some(ty)
-                });
-                let ty = match ty {
-                    Some(ty) => {
-                        let adjustments = auto_deref_adjust_steps(&autoderef);
-                        self.write_expr_adj(*expr, adjustments);
-                        let ty = self.insert_type_vars(ty);
-                        let ty = self.normalize_associated_types_in(ty);
-                        ty
-                    }
-                    _ => {
-                        // Write down the first private field resolution if we found no field
-                        // This aids IDE features for private fields like goto def
-                        if let Some(field) = private_field {
-                            self.result.field_resolutions.insert(tgt_expr, field);
-                            self.result
-                                .diagnostics
-                                .push(InferenceDiagnostic::PrivateField { expr: tgt_expr, field });
-                        }
-                        self.err_ty()
-                    }
-                };
-                ty
-            }
+            Expr::Field { expr, name } => self.infer_field_access(tgt_expr, *expr, name),
             Expr::Await { expr } => {
                 let inner_ty = self.infer_expr_inner(*expr, &Expectation::none());
                 self.resolve_associated_type(inner_ty, self.resolve_future_future_output())
@@ -611,7 +598,7 @@ impl<'a> InferenceContext<'a> {
             Expr::Cast { expr, type_ref } => {
                 let cast_ty = self.make_ty(type_ref);
                 // FIXME: propagate the "castable to" expectation
-                let _inner_ty = self.infer_expr_inner(*expr, &Expectation::None);
+                let _inner_ty = self.infer_expr_no_expect(*expr);
                 // FIXME check the cast...
                 cast_ty
             }
@@ -807,49 +794,7 @@ impl<'a> InferenceContext<'a> {
 
                 TyKind::Tuple(tys.len(), Substitution::from_iter(Interner, tys)).intern(Interner)
             }
-            Expr::Array(array) => {
-                let elem_ty =
-                    match expected.to_option(&mut self.table).as_ref().map(|t| t.kind(Interner)) {
-                        Some(TyKind::Array(st, _) | TyKind::Slice(st)) => st.clone(),
-                        _ => self.table.new_type_var(),
-                    };
-                let mut coerce = CoerceMany::new(elem_ty.clone());
-
-                let expected = Expectation::has_type(elem_ty.clone());
-                let len = match array {
-                    Array::ElementList { elements, .. } => {
-                        for &expr in elements.iter() {
-                            let cur_elem_ty = self.infer_expr_inner(expr, &expected);
-                            coerce.coerce(self, Some(expr), &cur_elem_ty);
-                        }
-                        consteval::usize_const(Some(elements.len() as u128))
-                    }
-                    &Array::Repeat { initializer, repeat } => {
-                        self.infer_expr_coerce(initializer, &Expectation::has_type(elem_ty));
-                        self.infer_expr(
-                            repeat,
-                            &Expectation::HasType(
-                                TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(Interner),
-                            ),
-                        );
-
-                        if let Some(g_def) = self.owner.as_generic_def_id() {
-                            let generics = generics(self.db.upcast(), g_def);
-                            consteval::eval_to_const(
-                                repeat,
-                                ParamLoweringMode::Placeholder,
-                                self,
-                                || generics,
-                                DebruijnIndex::INNERMOST,
-                            )
-                        } else {
-                            consteval::usize_const(None)
-                        }
-                    }
-                };
-
-                TyKind::Array(coerce.complete(), len).intern(Interner)
-            }
+            Expr::Array(array) => self.infer_expr_array(array, expected),
             Expr::Literal(lit) => match lit {
                 Literal::Bool(..) => self.result.standard_types.bool_.clone(),
                 Literal::String(..) => {
@@ -859,7 +804,11 @@ impl<'a> InferenceContext<'a> {
                 Literal::ByteString(bs) => {
                     let byte_type = TyKind::Scalar(Scalar::Uint(UintTy::U8)).intern(Interner);
 
-                    let len = consteval::usize_const(Some(bs.len() as u128));
+                    let len = consteval::usize_const(
+                        self.db,
+                        Some(bs.len() as u128),
+                        self.resolver.krate(),
+                    );
 
                     let array_type = TyKind::Array(byte_type, len).intern(Interner);
                     TyKind::Ref(Mutability::Not, static_lifetime(), array_type).intern(Interner)
@@ -904,6 +853,97 @@ impl<'a> InferenceContext<'a> {
         ty
     }
 
+    fn infer_expr_array(
+        &mut self,
+        array: &Array,
+        expected: &Expectation,
+    ) -> chalk_ir::Ty<Interner> {
+        let elem_ty = match expected.to_option(&mut self.table).as_ref().map(|t| t.kind(Interner)) {
+            Some(TyKind::Array(st, _) | TyKind::Slice(st)) => st.clone(),
+            _ => self.table.new_type_var(),
+        };
+
+        let krate = self.resolver.krate();
+
+        let expected = Expectation::has_type(elem_ty.clone());
+        let (elem_ty, len) = match array {
+            Array::ElementList { elements, .. } if elements.is_empty() => {
+                (elem_ty, consteval::usize_const(self.db, Some(0), krate))
+            }
+            Array::ElementList { elements, .. } => {
+                let mut coerce = CoerceMany::new(elem_ty.clone());
+                for &expr in elements.iter() {
+                    let cur_elem_ty = self.infer_expr_inner(expr, &expected);
+                    coerce.coerce(self, Some(expr), &cur_elem_ty);
+                }
+                (
+                    coerce.complete(self),
+                    consteval::usize_const(self.db, Some(elements.len() as u128), krate),
+                )
+            }
+            &Array::Repeat { initializer, repeat } => {
+                self.infer_expr_coerce(initializer, &Expectation::has_type(elem_ty.clone()));
+                self.infer_expr(
+                    repeat,
+                    &Expectation::HasType(
+                        TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(Interner),
+                    ),
+                );
+
+                (
+                    elem_ty,
+                    if let Some(g_def) = self.owner.as_generic_def_id() {
+                        let generics = generics(self.db.upcast(), g_def);
+                        consteval::eval_to_const(
+                            repeat,
+                            ParamLoweringMode::Placeholder,
+                            self,
+                            || generics,
+                            DebruijnIndex::INNERMOST,
+                        )
+                    } else {
+                        consteval::usize_const(self.db, None, krate)
+                    },
+                )
+            }
+        };
+
+        TyKind::Array(elem_ty, len).intern(Interner)
+    }
+
+    pub(super) fn infer_return(&mut self, expr: ExprId) {
+        let ret_ty = self
+            .return_coercion
+            .as_mut()
+            .expect("infer_return called outside function body")
+            .expected_ty();
+        let return_expr_ty = self.infer_expr_inner(expr, &Expectation::HasType(ret_ty));
+        let mut coerce_many = self.return_coercion.take().unwrap();
+        coerce_many.coerce(self, Some(expr), &return_expr_ty);
+        self.return_coercion = Some(coerce_many);
+    }
+
+    fn infer_expr_return(&mut self, expr: Option<ExprId>) -> Ty {
+        match self.return_coercion {
+            Some(_) => {
+                if let Some(expr) = expr {
+                    self.infer_return(expr);
+                } else {
+                    let mut coerce = self.return_coercion.take().unwrap();
+                    coerce.coerce_forced_unit(self);
+                    self.return_coercion = Some(coerce);
+                }
+            }
+            None => {
+                // FIXME: diagnose return outside of function
+                if let Some(expr) = expr {
+                    self.infer_expr_no_expect(expr);
+                }
+            }
+        }
+        self.result.standard_types.never.clone()
+    }
+
     fn infer_expr_box(&mut self, inner_expr: ExprId, expected: &Expectation) -> Ty {
         if let Some(box_id) = self.resolve_boxed_box() {
             let table = &mut self.table;
@@ -982,8 +1022,11 @@ impl<'a> InferenceContext<'a> {
                     // type and length). This should not be just an error type,
                     // because we are to compute the unifiability of this type and
                     // `rhs_ty` in the end of this function to issue type mismatches.
-                    _ => TyKind::Array(self.err_ty(), crate::consteval::usize_const(None))
-                        .intern(Interner),
+                    _ => TyKind::Array(
+                        self.err_ty(),
+                        crate::consteval::usize_const(self.db, None, self.resolver.krate()),
+                    )
+                    .intern(Interner),
                 }
             }
             Expr::RecordLit { path, fields, .. } => {
@@ -1123,65 +1166,211 @@ impl<'a> InferenceContext<'a> {
         expr: ExprId,
         statements: &[Statement],
         tail: Option<ExprId>,
+        label: Option<LabelId>,
         expected: &Expectation,
     ) -> Ty {
-        for stmt in statements {
-            match stmt {
-                Statement::Let { pat, type_ref, initializer, else_branch } => {
-                    let decl_ty = type_ref
-                        .as_ref()
-                        .map(|tr| self.make_ty(tr))
-                        .unwrap_or_else(|| self.err_ty());
-
-                    // Always use the declared type when specified
-                    let mut ty = decl_ty.clone();
-
-                    if let Some(expr) = initializer {
-                        let actual_ty =
-                            self.infer_expr_coerce(*expr, &Expectation::has_type(decl_ty.clone()));
-                        if decl_ty.is_unknown() {
-                            ty = actual_ty;
+        let coerce_ty = expected.coercion_target_type(&mut self.table);
+        let g = self.resolver.update_to_inner_scope(self.db.upcast(), self.owner, expr);
+
+        let (break_ty, ty) =
+            self.with_breakable_ctx(BreakableKind::Block, Some(coerce_ty.clone()), label, |this| {
+                for stmt in statements {
+                    match stmt {
+                        Statement::Let { pat, type_ref, initializer, else_branch } => {
+                            let decl_ty = type_ref
+                                .as_ref()
+                                .map(|tr| this.make_ty(tr))
+                                .unwrap_or_else(|| this.table.new_type_var());
+
+                            let ty = if let Some(expr) = initializer {
+                                let ty = if contains_explicit_ref_binding(&this.body, *pat) {
+                                    this.infer_expr(*expr, &Expectation::has_type(decl_ty.clone()))
+                                } else {
+                                    this.infer_expr_coerce(
+                                        *expr,
+                                        &Expectation::has_type(decl_ty.clone()),
+                                    )
+                                };
+                                if type_ref.is_some() {
+                                    decl_ty
+                                } else {
+                                    ty
+                                }
+                            } else {
+                                decl_ty
+                            };
+
+                            this.infer_top_pat(*pat, &ty);
+
+                            if let Some(expr) = else_branch {
+                                let previous_diverges =
+                                    mem::replace(&mut this.diverges, Diverges::Maybe);
+                                this.infer_expr_coerce(
+                                    *expr,
+                                    &Expectation::HasType(this.result.standard_types.never.clone()),
+                                );
+                                this.diverges = previous_diverges;
+                            }
+                        }
+                        &Statement::Expr { expr, has_semi } => {
+                            if has_semi {
+                                this.infer_expr(expr, &Expectation::none());
+                            } else {
+                                this.infer_expr_coerce(
+                                    expr,
+                                    &Expectation::HasType(this.result.standard_types.unit.clone()),
+                                );
+                            }
                         }
                     }
+                }
 
-                    if let Some(expr) = else_branch {
-                        self.infer_expr_coerce(
-                            *expr,
-                            &Expectation::HasType(self.result.standard_types.never.clone()),
-                        );
+                // FIXME: This should make use of the breakable CoerceMany
+                if let Some(expr) = tail {
+                    this.infer_expr_coerce(expr, expected)
+                } else {
+                    // Citing rustc: if there is no explicit tail expression,
+                    // that is typically equivalent to a tail expression
+                    // of `()` -- except if the block diverges. In that
+                    // case, there is no value supplied from the tail
+                    // expression (assuming there are no other breaks,
+                    // this implies that the type of the block will be
+                    // `!`).
+                    if this.diverges.is_always() {
+                        // we don't even make an attempt at coercion
+                        this.table.new_maybe_never_var()
+                    } else if let Some(t) = expected.only_has_type(&mut this.table) {
+                        if this
+                            .coerce(Some(expr), &this.result.standard_types.unit.clone(), &t)
+                            .is_err()
+                        {
+                            this.result.type_mismatches.insert(
+                                expr.into(),
+                                TypeMismatch {
+                                    expected: t.clone(),
+                                    actual: this.result.standard_types.unit.clone(),
+                                },
+                            );
+                        }
+                        t
+                    } else {
+                        this.result.standard_types.unit.clone()
                     }
+                }
+            });
+        self.resolver.reset_to_guard(g);
+
+        break_ty.unwrap_or(ty)
+    }
 
-                    self.infer_pat(*pat, &ty, BindingMode::default());
+    fn lookup_field(
+        &mut self,
+        receiver_ty: &Ty,
+        name: &Name,
+    ) -> Option<(Ty, Option<FieldId>, Vec<Adjustment>, bool)> {
+        let mut autoderef = Autoderef::new(&mut self.table, receiver_ty.clone());
+        let mut private_field = None;
+        let res = autoderef.by_ref().find_map(|(derefed_ty, _)| {
+            let (field_id, parameters) = match derefed_ty.kind(Interner) {
+                TyKind::Tuple(_, substs) => {
+                    return name.as_tuple_index().and_then(|idx| {
+                        substs
+                            .as_slice(Interner)
+                            .get(idx)
+                            .map(|a| a.assert_ty_ref(Interner))
+                            .cloned()
+                            .map(|ty| (None, ty))
+                    });
                 }
-                Statement::Expr { expr, .. } => {
-                    self.infer_expr(*expr, &Expectation::none());
+                TyKind::Adt(AdtId(hir_def::AdtId::StructId(s)), parameters) => {
+                    let local_id = self.db.struct_data(*s).variant_data.field(name)?;
+                    let field = FieldId { parent: (*s).into(), local_id };
+                    (field, parameters.clone())
                 }
+                TyKind::Adt(AdtId(hir_def::AdtId::UnionId(u)), parameters) => {
+                    let local_id = self.db.union_data(*u).variant_data.field(name)?;
+                    let field = FieldId { parent: (*u).into(), local_id };
+                    (field, parameters.clone())
+                }
+                _ => return None,
+            };
+            let is_visible = self.db.field_visibilities(field_id.parent)[field_id.local_id]
+                .is_visible_from(self.db.upcast(), self.resolver.module());
+            if !is_visible {
+                if private_field.is_none() {
+                    private_field = Some((field_id, parameters));
+                }
+                return None;
             }
-        }
+            let ty = self.db.field_types(field_id.parent)[field_id.local_id]
+                .clone()
+                .substitute(Interner, &parameters);
+            Some((Some(field_id), ty))
+        });
 
-        if let Some(expr) = tail {
-            self.infer_expr_coerce(expr, expected)
-        } else {
-            // Citing rustc: if there is no explicit tail expression,
-            // that is typically equivalent to a tail expression
-            // of `()` -- except if the block diverges. In that
-            // case, there is no value supplied from the tail
-            // expression (assuming there are no other breaks,
-            // this implies that the type of the block will be
-            // `!`).
-            if self.diverges.is_always() {
-                // we don't even make an attempt at coercion
-                self.table.new_maybe_never_var()
-            } else if let Some(t) = expected.only_has_type(&mut self.table) {
-                if self.coerce(Some(expr), &TyBuilder::unit(), &t).is_err() {
-                    self.result.type_mismatches.insert(
-                        expr.into(),
-                        TypeMismatch { expected: t.clone(), actual: TyBuilder::unit() },
-                    );
+        Some(match res {
+            Some((field_id, ty)) => {
+                let adjustments = auto_deref_adjust_steps(&autoderef);
+                let ty = self.insert_type_vars(ty);
+                let ty = self.normalize_associated_types_in(ty);
+
+                (ty, field_id, adjustments, true)
+            }
+            None => {
+                let (field_id, subst) = private_field?;
+                let adjustments = auto_deref_adjust_steps(&autoderef);
+                let ty = self.db.field_types(field_id.parent)[field_id.local_id]
+                    .clone()
+                    .substitute(Interner, &subst);
+                let ty = self.insert_type_vars(ty);
+                let ty = self.normalize_associated_types_in(ty);
+
+                (ty, Some(field_id), adjustments, false)
+            }
+        })
+    }
+
+    fn infer_field_access(&mut self, tgt_expr: ExprId, receiver: ExprId, name: &Name) -> Ty {
+        let receiver_ty = self.infer_expr_inner(receiver, &Expectation::none());
+        match self.lookup_field(&receiver_ty, name) {
+            Some((ty, field_id, adjustments, is_public)) => {
+                self.write_expr_adj(receiver, adjustments);
+                if let Some(field_id) = field_id {
+                    self.result.field_resolutions.insert(tgt_expr, field_id);
                 }
-                t
-            } else {
-                TyBuilder::unit()
+                if !is_public {
+                    if let Some(field) = field_id {
+                        // FIXME: Merge this diagnostic into UnresolvedField?
+                        self.result
+                            .diagnostics
+                            .push(InferenceDiagnostic::PrivateField { expr: tgt_expr, field });
+                    }
+                }
+                ty
+            }
+            None => {
+                // no field found,
+                let method_with_same_name_exists = {
+                    self.get_traits_in_scope();
+
+                    let canonicalized_receiver = self.canonicalize(receiver_ty.clone());
+                    method_resolution::lookup_method(
+                        self.db,
+                        &canonicalized_receiver.value,
+                        self.trait_env.clone(),
+                        self.get_traits_in_scope().as_ref().left_or_else(|&it| it),
+                        VisibleFromModule::Filter(self.resolver.module()),
+                        name,
+                    )
+                    .is_some()
+                };
+                self.result.diagnostics.push(InferenceDiagnostic::UnresolvedField {
+                    expr: tgt_expr,
+                    receiver: receiver_ty,
+                    name: name.clone(),
+                    method_with_same_name_exists,
+                });
+                self.err_ty()
             }
         }
     }
@@ -1198,13 +1387,11 @@ impl<'a> InferenceContext<'a> {
         let receiver_ty = self.infer_expr(receiver, &Expectation::none());
         let canonicalized_receiver = self.canonicalize(receiver_ty.clone());
 
-        let traits_in_scope = self.resolver.traits_in_scope(self.db.upcast());
-
         let resolved = method_resolution::lookup_method(
             self.db,
             &canonicalized_receiver.value,
             self.trait_env.clone(),
-            &traits_in_scope,
+            self.get_traits_in_scope().as_ref().left_or_else(|&it| it),
             VisibleFromModule::Filter(self.resolver.module()),
             method_name,
         );
@@ -1223,11 +1410,30 @@ impl<'a> InferenceContext<'a> {
                 }
                 (ty, self.db.value_ty(func.into()), substs)
             }
-            None => (
-                receiver_ty,
-                Binders::empty(Interner, self.err_ty()),
-                Substitution::empty(Interner),
-            ),
+            None => {
+                let field_with_same_name_exists = match self.lookup_field(&receiver_ty, method_name)
+                {
+                    Some((ty, field_id, adjustments, _public)) => {
+                        self.write_expr_adj(receiver, adjustments);
+                        if let Some(field_id) = field_id {
+                            self.result.field_resolutions.insert(tgt_expr, field_id);
+                        }
+                        Some(ty)
+                    }
+                    None => None,
+                };
+                self.result.diagnostics.push(InferenceDiagnostic::UnresolvedMethodCall {
+                    expr: tgt_expr,
+                    receiver: receiver_ty.clone(),
+                    name: method_name.clone(),
+                    field_with_same_name: field_with_same_name_exists,
+                });
+                (
+                    receiver_ty,
+                    Binders::empty(Interner, self.err_ty()),
+                    Substitution::empty(Interner),
+                )
+            }
         };
         let method_ty = method_ty.substitute(Interner, &substs);
         self.register_obligations_for_call(&method_ty);
@@ -1636,16 +1842,16 @@ impl<'a> InferenceContext<'a> {
     fn with_breakable_ctx<T>(
         &mut self,
         kind: BreakableKind,
-        ty: Ty,
+        ty: Option<Ty>,
         label: Option<LabelId>,
         cb: impl FnOnce(&mut Self) -> T,
     ) -> (Option<Ty>, T) {
         self.breakables.push({
             let label = label.map(|label| self.body[label].name.clone());
-            BreakableContext { kind, may_break: false, coerce: CoerceMany::new(ty), label }
+            BreakableContext { kind, may_break: false, coerce: ty.map(CoerceMany::new), label }
         });
         let res = cb(self);
         let ctx = self.breakables.pop().expect("breakable stack broken");
-        (ctx.may_break.then(|| ctx.coerce.complete()), res)
+        (if ctx.may_break { ctx.coerce.map(|ctx| ctx.complete(self)) } else { None }, res)
     }
 }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs
index f154dac8e87..0f49e837881 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs
@@ -4,22 +4,60 @@ use std::iter::repeat_with;
 
 use chalk_ir::Mutability;
 use hir_def::{
-    expr::{BindingAnnotation, Expr, Literal, Pat, PatId},
+    body::Body,
+    expr::{
+        Binding, BindingAnnotation, BindingId, Expr, ExprId, ExprOrPatId, Literal, Pat, PatId,
+        RecordFieldPat,
+    },
     path::Path,
-    type_ref::ConstScalar,
 };
 use hir_expand::name::Name;
 
 use crate::{
-    consteval::intern_const_scalar,
+    consteval::{try_const_usize, usize_const},
     infer::{BindingMode, Expectation, InferenceContext, TypeMismatch},
     lower::lower_to_chalk_mutability,
     primitive::UintTy,
-    static_lifetime, ConcreteConst, ConstValue, Interner, Scalar, Substitution, Ty, TyBuilder,
-    TyExt, TyKind,
+    static_lifetime, Interner, Scalar, Substitution, Ty, TyBuilder, TyExt, TyKind,
 };
 
-use super::PatLike;
+/// Used to generalize patterns and assignee expressions.
+pub(super) trait PatLike: Into<ExprOrPatId> + Copy {
+    type BindingMode: Copy;
+
+    fn infer(
+        this: &mut InferenceContext<'_>,
+        id: Self,
+        expected_ty: &Ty,
+        default_bm: Self::BindingMode,
+    ) -> Ty;
+}
+
+impl PatLike for ExprId {
+    type BindingMode = ();
+
+    fn infer(
+        this: &mut InferenceContext<'_>,
+        id: Self,
+        expected_ty: &Ty,
+        (): Self::BindingMode,
+    ) -> Ty {
+        this.infer_assignee_expr(id, expected_ty)
+    }
+}
+
+impl PatLike for PatId {
+    type BindingMode = BindingMode;
+
+    fn infer(
+        this: &mut InferenceContext<'_>,
+        id: Self,
+        expected_ty: &Ty,
+        default_bm: Self::BindingMode,
+    ) -> Ty {
+        this.infer_pat(id, expected_ty, default_bm)
+    }
+}
 
 impl<'a> InferenceContext<'a> {
     /// Infers type for tuple struct pattern or its corresponding assignee expression.
@@ -112,6 +150,7 @@ impl<'a> InferenceContext<'a> {
         ellipsis: Option<usize>,
         subs: &[T],
     ) -> Ty {
+        let expected = self.resolve_ty_shallow(expected);
         let expectations = match expected.as_tuple() {
             Some(parameters) => &*parameters.as_slice(Interner),
             _ => &[],
@@ -145,12 +184,11 @@ impl<'a> InferenceContext<'a> {
             .intern(Interner)
     }
 
-    pub(super) fn infer_pat(
-        &mut self,
-        pat: PatId,
-        expected: &Ty,
-        mut default_bm: BindingMode,
-    ) -> Ty {
+    pub(super) fn infer_top_pat(&mut self, pat: PatId, expected: &Ty) {
+        self.infer_pat(pat, expected, BindingMode::default());
+    }
+
+    fn infer_pat(&mut self, pat: PatId, expected: &Ty, mut default_bm: BindingMode) -> Ty {
         let mut expected = self.resolve_ty_shallow(expected);
 
         if is_non_ref_pat(self.body, pat) {
@@ -185,30 +223,17 @@ impl<'a> InferenceContext<'a> {
                 self.infer_tuple_pat_like(&expected, default_bm, *ellipsis, args)
             }
             Pat::Or(pats) => {
-                if let Some((first_pat, rest)) = pats.split_first() {
-                    let ty = self.infer_pat(*first_pat, &expected, default_bm);
-                    for pat in rest {
-                        self.infer_pat(*pat, &expected, default_bm);
-                    }
-                    ty
-                } else {
-                    self.err_ty()
+                for pat in pats.iter() {
+                    self.infer_pat(*pat, &expected, default_bm);
                 }
+                expected.clone()
             }
-            Pat::Ref { pat, mutability } => {
-                let mutability = lower_to_chalk_mutability(*mutability);
-                let expectation = match expected.as_reference() {
-                    Some((inner_ty, _lifetime, exp_mut)) => {
-                        if mutability != exp_mut {
-                            // FIXME: emit type error?
-                        }
-                        inner_ty.clone()
-                    }
-                    _ => self.result.standard_types.unknown.clone(),
-                };
-                let subty = self.infer_pat(*pat, &expectation, default_bm);
-                TyKind::Ref(mutability, static_lifetime(), subty).intern(Interner)
-            }
+            &Pat::Ref { pat, mutability } => self.infer_ref_pat(
+                pat,
+                lower_to_chalk_mutability(mutability),
+                &expected,
+                default_bm,
+            ),
             Pat::TupleStruct { path: p, args: subpats, ellipsis } => self
                 .infer_tuple_struct_pat_like(
                     p.as_deref(),
@@ -223,72 +248,14 @@ impl<'a> InferenceContext<'a> {
                 self.infer_record_pat_like(p.as_deref(), &expected, default_bm, pat, subs)
             }
             Pat::Path(path) => {
-                // FIXME use correct resolver for the surrounding expression
-                let resolver = self.resolver.clone();
-                self.infer_path(&resolver, path, pat.into()).unwrap_or_else(|| self.err_ty())
+                // FIXME update resolver for the surrounding expression
+                self.infer_path(path, pat.into()).unwrap_or_else(|| self.err_ty())
             }
-            Pat::Bind { mode, name: _, subpat } => {
-                let mode = if mode == &BindingAnnotation::Unannotated {
-                    default_bm
-                } else {
-                    BindingMode::convert(*mode)
-                };
-                self.result.pat_binding_modes.insert(pat, mode);
-
-                let inner_ty = match subpat {
-                    Some(subpat) => self.infer_pat(*subpat, &expected, default_bm),
-                    None => expected,
-                };
-                let inner_ty = self.insert_type_vars_shallow(inner_ty);
-
-                let bound_ty = match mode {
-                    BindingMode::Ref(mutability) => {
-                        TyKind::Ref(mutability, static_lifetime(), inner_ty.clone())
-                            .intern(Interner)
-                    }
-                    BindingMode::Move => inner_ty.clone(),
-                };
-                self.write_pat_ty(pat, bound_ty);
-                return inner_ty;
+            Pat::Bind { id, subpat } => {
+                return self.infer_bind_pat(pat, *id, default_bm, *subpat, &expected);
             }
             Pat::Slice { prefix, slice, suffix } => {
-                let elem_ty = match expected.kind(Interner) {
-                    TyKind::Array(st, _) | TyKind::Slice(st) => st.clone(),
-                    _ => self.err_ty(),
-                };
-
-                for &pat_id in prefix.iter().chain(suffix.iter()) {
-                    self.infer_pat(pat_id, &elem_ty, default_bm);
-                }
-
-                if let &Some(slice_pat_id) = slice {
-                    let rest_pat_ty = match expected.kind(Interner) {
-                        TyKind::Array(_, length) => {
-                            let len = match length.data(Interner).value {
-                                ConstValue::Concrete(ConcreteConst {
-                                    interned: ConstScalar::UInt(len),
-                                }) => len.checked_sub((prefix.len() + suffix.len()) as u128),
-                                _ => None,
-                            };
-                            TyKind::Array(
-                                elem_ty.clone(),
-                                intern_const_scalar(
-                                    len.map_or(ConstScalar::Unknown, |len| ConstScalar::UInt(len)),
-                                    TyBuilder::usize(),
-                                ),
-                            )
-                        }
-                        _ => TyKind::Slice(elem_ty.clone()),
-                    }
-                    .intern(Interner);
-                    self.infer_pat(slice_pat_id, &rest_pat_ty, default_bm);
-                }
-
-                match expected.kind(Interner) {
-                    TyKind::Array(_, const_) => TyKind::Array(elem_ty, const_.clone()),
-                    _ => TyKind::Slice(elem_ty),
-                }
-                .intern(Interner)
+                self.infer_slice_pat(&expected, prefix, slice, suffix, default_bm)
             }
             Pat::Wild => expected.clone(),
             Pat::Range { start, end } => {
@@ -296,27 +263,10 @@ impl<'a> InferenceContext<'a> {
                 self.infer_expr(*end, &Expectation::has_type(start_ty))
             }
             &Pat::Lit(expr) => {
-                // FIXME: using `Option` here is a workaround until we can use if-let chains in stable.
-                let mut pat_ty = None;
-
-                // Like slice patterns, byte string patterns can denote both `&[u8; N]` and `&[u8]`.
-                if let Expr::Literal(Literal::ByteString(_)) = self.body[expr] {
-                    if let Some((inner, ..)) = expected.as_reference() {
-                        let inner = self.resolve_ty_shallow(inner);
-                        if matches!(inner.kind(Interner), TyKind::Slice(_)) {
-                            let elem_ty = TyKind::Scalar(Scalar::Uint(UintTy::U8)).intern(Interner);
-                            let slice_ty = TyKind::Slice(elem_ty).intern(Interner);
-                            let ty = TyKind::Ref(Mutability::Not, static_lifetime(), slice_ty)
-                                .intern(Interner);
-                            self.write_expr_ty(expr, ty.clone());
-                            pat_ty = Some(ty);
-                        }
-                    }
-                }
-
-                pat_ty.unwrap_or_else(|| {
-                    self.infer_expr(expr, &Expectation::has_type(expected.clone()))
-                })
+                // Don't emit type mismatches again, the expression lowering already did that.
+                let ty = self.infer_lit_pat(expr, &expected);
+                self.write_pat_ty(pat, ty.clone());
+                return ty;
             }
             Pat::Box { inner } => match self.resolve_boxed_box() {
                 Some(box_adt) => {
@@ -345,7 +295,8 @@ impl<'a> InferenceContext<'a> {
         };
         // use a new type variable if we got error type here
         let ty = self.insert_type_vars_shallow(ty);
-        if !self.unify(&ty, &expected) {
+        // FIXME: This never check is odd, but required with out we do inference right now
+        if !expected.is_never() && !self.unify(&ty, &expected) {
             self.result
                 .type_mismatches
                 .insert(pat.into(), TypeMismatch { expected, actual: ty.clone() });
@@ -353,6 +304,111 @@ impl<'a> InferenceContext<'a> {
         self.write_pat_ty(pat, ty.clone());
         ty
     }
+
+    fn infer_ref_pat(
+        &mut self,
+        pat: PatId,
+        mutability: Mutability,
+        expected: &Ty,
+        default_bm: BindingMode,
+    ) -> Ty {
+        let expectation = match expected.as_reference() {
+            Some((inner_ty, _lifetime, _exp_mut)) => inner_ty.clone(),
+            _ => self.result.standard_types.unknown.clone(),
+        };
+        let subty = self.infer_pat(pat, &expectation, default_bm);
+        TyKind::Ref(mutability, static_lifetime(), subty).intern(Interner)
+    }
+
+    fn infer_bind_pat(
+        &mut self,
+        pat: PatId,
+        binding: BindingId,
+        default_bm: BindingMode,
+        subpat: Option<PatId>,
+        expected: &Ty,
+    ) -> Ty {
+        let Binding { mode, .. } = self.body.bindings[binding];
+        let mode = if mode == BindingAnnotation::Unannotated {
+            default_bm
+        } else {
+            BindingMode::convert(mode)
+        };
+        self.result.pat_binding_modes.insert(pat, mode);
+
+        let inner_ty = match subpat {
+            Some(subpat) => self.infer_pat(subpat, &expected, default_bm),
+            None => expected.clone(),
+        };
+        let inner_ty = self.insert_type_vars_shallow(inner_ty);
+
+        let bound_ty = match mode {
+            BindingMode::Ref(mutability) => {
+                TyKind::Ref(mutability, static_lifetime(), inner_ty.clone()).intern(Interner)
+            }
+            BindingMode::Move => inner_ty.clone(),
+        };
+        self.write_pat_ty(pat, bound_ty.clone());
+        self.write_binding_ty(binding, bound_ty);
+        return inner_ty;
+    }
+
+    fn infer_slice_pat(
+        &mut self,
+        expected: &Ty,
+        prefix: &[PatId],
+        slice: &Option<PatId>,
+        suffix: &[PatId],
+        default_bm: BindingMode,
+    ) -> Ty {
+        let elem_ty = match expected.kind(Interner) {
+            TyKind::Array(st, _) | TyKind::Slice(st) => st.clone(),
+            _ => self.err_ty(),
+        };
+
+        for &pat_id in prefix.iter().chain(suffix.iter()) {
+            self.infer_pat(pat_id, &elem_ty, default_bm);
+        }
+
+        if let &Some(slice_pat_id) = slice {
+            let rest_pat_ty = match expected.kind(Interner) {
+                TyKind::Array(_, length) => {
+                    let len = try_const_usize(length);
+                    let len =
+                        len.and_then(|len| len.checked_sub((prefix.len() + suffix.len()) as u128));
+                    TyKind::Array(elem_ty.clone(), usize_const(self.db, len, self.resolver.krate()))
+                }
+                _ => TyKind::Slice(elem_ty.clone()),
+            }
+            .intern(Interner);
+            self.infer_pat(slice_pat_id, &rest_pat_ty, default_bm);
+        }
+
+        match expected.kind(Interner) {
+            TyKind::Array(_, const_) => TyKind::Array(elem_ty, const_.clone()),
+            _ => TyKind::Slice(elem_ty),
+        }
+        .intern(Interner)
+    }
+
+    fn infer_lit_pat(&mut self, expr: ExprId, expected: &Ty) -> Ty {
+        // Like slice patterns, byte string patterns can denote both `&[u8; N]` and `&[u8]`.
+        if let Expr::Literal(Literal::ByteString(_)) = self.body[expr] {
+            if let Some((inner, ..)) = expected.as_reference() {
+                let inner = self.resolve_ty_shallow(inner);
+                if matches!(inner.kind(Interner), TyKind::Slice(_)) {
+                    let elem_ty = TyKind::Scalar(Scalar::Uint(UintTy::U8)).intern(Interner);
+                    let slice_ty = TyKind::Slice(elem_ty).intern(Interner);
+                    let ty =
+                        TyKind::Ref(Mutability::Not, static_lifetime(), slice_ty).intern(Interner);
+                    self.write_expr_ty(expr, ty.clone());
+                    return ty;
+                }
+            }
+        }
+
+        self.infer_expr(expr, &Expectation::has_type(expected.clone()))
+    }
 }
 
 fn is_non_ref_pat(body: &hir_def::body::Body, pat: PatId) -> bool {
@@ -369,11 +425,52 @@ fn is_non_ref_pat(body: &hir_def::body::Body, pat: PatId) -> bool {
         Pat::Lit(expr) => {
             !matches!(body[*expr], Expr::Literal(Literal::String(..) | Literal::ByteString(..)))
         }
-        Pat::Bind {
-            mode: BindingAnnotation::Mutable | BindingAnnotation::Unannotated,
-            subpat: Some(subpat),
-            ..
-        } => is_non_ref_pat(body, *subpat),
+        Pat::Bind { id, subpat: Some(subpat), .. }
+            if matches!(
+                body.bindings[*id].mode,
+                BindingAnnotation::Mutable | BindingAnnotation::Unannotated
+            ) =>
+        {
+            is_non_ref_pat(body, *subpat)
+        }
         Pat::Wild | Pat::Bind { .. } | Pat::Ref { .. } | Pat::Box { .. } | Pat::Missing => false,
     }
 }
+
+pub(super) fn contains_explicit_ref_binding(body: &Body, pat_id: PatId) -> bool {
+    let mut res = false;
+    walk_pats(body, pat_id, &mut |pat| {
+        res |= matches!(pat, Pat::Bind { id, .. } if body.bindings[*id].mode == BindingAnnotation::Ref);
+    });
+    res
+}
+
+fn walk_pats(body: &Body, pat_id: PatId, f: &mut impl FnMut(&Pat)) {
+    let pat = &body[pat_id];
+    f(pat);
+    match pat {
+        Pat::Range { .. }
+        | Pat::Lit(..)
+        | Pat::Path(..)
+        | Pat::ConstBlock(..)
+        | Pat::Wild
+        | Pat::Missing => {}
+        &Pat::Bind { subpat, .. } => {
+            if let Some(subpat) = subpat {
+                walk_pats(body, subpat, f);
+            }
+        }
+        Pat::Or(args) | Pat::Tuple { args, .. } | Pat::TupleStruct { args, .. } => {
+            args.iter().copied().for_each(|p| walk_pats(body, p, f));
+        }
+        Pat::Ref { pat, .. } => walk_pats(body, *pat, f),
+        Pat::Slice { prefix, slice, suffix } => {
+            let total_iter = prefix.iter().chain(slice.iter()).chain(suffix.iter());
+            total_iter.copied().for_each(|p| walk_pats(body, p, f));
+        }
+        Pat::Record { args, .. } => {
+            args.iter().for_each(|RecordFieldPat { pat, .. }| walk_pats(body, *pat, f));
+        }
+        Pat::Box { inner } => walk_pats(body, *inner, f),
+    }
+}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs
index 0a8527afbd0..2267fedaa8e 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs
@@ -3,7 +3,7 @@
 use chalk_ir::cast::Cast;
 use hir_def::{
     path::{Path, PathSegment},
-    resolver::{ResolveValueResult, Resolver, TypeNs, ValueNs},
+    resolver::{ResolveValueResult, TypeNs, ValueNs},
     AdtId, AssocItemId, EnumVariantId, ItemContainerId, Lookup,
 };
 use hir_expand::name::Name;
@@ -21,55 +21,42 @@ use crate::{
 use super::{ExprOrPatId, InferenceContext, TraitRef};
 
 impl<'a> InferenceContext<'a> {
-    pub(super) fn infer_path(
-        &mut self,
-        resolver: &Resolver,
-        path: &Path,
-        id: ExprOrPatId,
-    ) -> Option<Ty> {
-        let ty = self.resolve_value_path(resolver, path, id)?;
+    pub(super) fn infer_path(&mut self, path: &Path, id: ExprOrPatId) -> Option<Ty> {
+        let ty = self.resolve_value_path(path, id)?;
         let ty = self.insert_type_vars(ty);
         let ty = self.normalize_associated_types_in(ty);
         Some(ty)
     }
 
-    fn resolve_value_path(
-        &mut self,
-        resolver: &Resolver,
-        path: &Path,
-        id: ExprOrPatId,
-    ) -> Option<Ty> {
+    fn resolve_value_path(&mut self, path: &Path, id: ExprOrPatId) -> Option<Ty> {
         let (value, self_subst) = if let Some(type_ref) = path.type_anchor() {
-            if path.segments().is_empty() {
-                // This can't actually happen syntax-wise
-                return None;
-            }
+            let Some(last) = path.segments().last() else { return None };
             let ty = self.make_ty(type_ref);
             let remaining_segments_for_ty = path.segments().take(path.segments().len() - 1);
-            let ctx = crate::lower::TyLoweringContext::new(self.db, resolver);
+            let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver);
             let (ty, _) = ctx.lower_ty_relative_path(ty, None, remaining_segments_for_ty);
-            self.resolve_ty_assoc_item(
-                ty,
-                path.segments().last().expect("path had at least one segment").name,
-                id,
-            )?
+            self.resolve_ty_assoc_item(ty, last.name, id).map(|(it, substs)| (it, Some(substs)))?
         } else {
+            // FIXME: report error, unresolved first path segment
             let value_or_partial =
-                resolver.resolve_path_in_value_ns(self.db.upcast(), path.mod_path())?;
+                self.resolver.resolve_path_in_value_ns(self.db.upcast(), path.mod_path())?;
 
             match value_or_partial {
                 ResolveValueResult::ValueNs(it) => (it, None),
-                ResolveValueResult::Partial(def, remaining_index) => {
-                    self.resolve_assoc_item(def, path, remaining_index, id)?
-                }
+                ResolveValueResult::Partial(def, remaining_index) => self
+                    .resolve_assoc_item(def, path, remaining_index, id)
+                    .map(|(it, substs)| (it, Some(substs)))?,
             }
         };
 
         let typable: ValueTyDefId = match value {
-            ValueNs::LocalBinding(pat) => {
-                let ty = self.result.type_of_pat.get(pat)?.clone();
-                return Some(ty);
-            }
+            ValueNs::LocalBinding(pat) => match self.result.type_of_binding.get(pat) {
+                Some(ty) => return Some(ty.clone()),
+                None => {
+                    never!("uninferred pattern?");
+                    return None;
+                }
+            },
             ValueNs::FunctionId(it) => it.into(),
             ValueNs::ConstId(it) => it.into(),
             ValueNs::StaticId(it) => it.into(),
@@ -91,7 +78,7 @@ impl<'a> InferenceContext<'a> {
                     let ty = self.db.value_ty(struct_id.into()).substitute(Interner, &substs);
                     return Some(ty);
                 } else {
-                    // FIXME: diagnostic, invalid Self reference
+                    // FIXME: report error, invalid Self reference
                     return None;
                 }
             }
@@ -126,7 +113,7 @@ impl<'a> InferenceContext<'a> {
         path: &Path,
         remaining_index: usize,
         id: ExprOrPatId,
-    ) -> Option<(ValueNs, Option<Substitution>)> {
+    ) -> Option<(ValueNs, Substitution)> {
         assert!(remaining_index < path.segments().len());
         // there may be more intermediate segments between the resolved one and
         // the end. Only the last segment needs to be resolved to a value; from
@@ -179,7 +166,7 @@ impl<'a> InferenceContext<'a> {
         trait_ref: TraitRef,
         segment: PathSegment<'_>,
         id: ExprOrPatId,
-    ) -> Option<(ValueNs, Option<Substitution>)> {
+    ) -> Option<(ValueNs, Substitution)> {
         let trait_ = trait_ref.hir_trait_id();
         let item =
             self.db.trait_data(trait_).items.iter().map(|(_name, id)| (*id)).find_map(|item| {
@@ -215,7 +202,7 @@ impl<'a> InferenceContext<'a> {
         };
 
         self.write_assoc_resolution(id, item, trait_ref.substitution.clone());
-        Some((def, Some(trait_ref.substitution)))
+        Some((def, trait_ref.substitution))
     }
 
     fn resolve_ty_assoc_item(
@@ -223,7 +210,7 @@ impl<'a> InferenceContext<'a> {
         ty: Ty,
         name: &Name,
         id: ExprOrPatId,
-    ) -> Option<(ValueNs, Option<Substitution>)> {
+    ) -> Option<(ValueNs, Substitution)> {
         if let TyKind::Error = ty.kind(Interner) {
             return None;
         }
@@ -233,70 +220,66 @@ impl<'a> InferenceContext<'a> {
         }
 
         let canonical_ty = self.canonicalize(ty.clone());
-        let traits_in_scope = self.resolver.traits_in_scope(self.db.upcast());
 
         let mut not_visible = None;
         let res = method_resolution::iterate_method_candidates(
             &canonical_ty.value,
             self.db,
             self.table.trait_env.clone(),
-            &traits_in_scope,
+            self.get_traits_in_scope().as_ref().left_or_else(|&it| it),
             VisibleFromModule::Filter(self.resolver.module()),
             Some(name),
             method_resolution::LookupMode::Path,
             |_ty, item, visible| {
-                let (def, container) = match item {
-                    AssocItemId::FunctionId(f) => {
-                        (ValueNs::FunctionId(f), f.lookup(self.db.upcast()).container)
-                    }
-                    AssocItemId::ConstId(c) => {
-                        (ValueNs::ConstId(c), c.lookup(self.db.upcast()).container)
-                    }
-                    AssocItemId::TypeAliasId(_) => unreachable!(),
-                };
-                let substs = match container {
-                    ItemContainerId::ImplId(impl_id) => {
-                        let impl_substs = TyBuilder::subst_for_def(self.db, impl_id, None)
-                            .fill_with_inference_vars(&mut self.table)
-                            .build();
-                        let impl_self_ty =
-                            self.db.impl_self_ty(impl_id).substitute(Interner, &impl_substs);
-                        self.unify(&impl_self_ty, &ty);
-                        impl_substs
-                    }
-                    ItemContainerId::TraitId(trait_) => {
-                        // we're picking this method
-                        let trait_ref = TyBuilder::trait_ref(self.db, trait_)
-                            .push(ty.clone())
-                            .fill_with_inference_vars(&mut self.table)
-                            .build();
-                        self.push_obligation(trait_ref.clone().cast(Interner));
-                        trait_ref.substitution
-                    }
-                    ItemContainerId::ModuleId(_) | ItemContainerId::ExternBlockId(_) => {
-                        never!("assoc item contained in module/extern block");
-                        return None;
-                    }
-                };
-
                 if visible {
-                    Some((def, item, Some(substs), true))
+                    Some((item, true))
                 } else {
                     if not_visible.is_none() {
-                        not_visible = Some((def, item, Some(substs), false));
+                        not_visible = Some((item, false));
                     }
                     None
                 }
             },
         );
         let res = res.or(not_visible);
-        if let Some((_, item, Some(ref substs), visible)) = res {
-            self.write_assoc_resolution(id, item, substs.clone());
-            if !visible {
-                self.push_diagnostic(InferenceDiagnostic::PrivateAssocItem { id, item })
+        let (item, visible) = res?;
+
+        let (def, container) = match item {
+            AssocItemId::FunctionId(f) => {
+                (ValueNs::FunctionId(f), f.lookup(self.db.upcast()).container)
+            }
+            AssocItemId::ConstId(c) => (ValueNs::ConstId(c), c.lookup(self.db.upcast()).container),
+            AssocItemId::TypeAliasId(_) => unreachable!(),
+        };
+        let substs = match container {
+            ItemContainerId::ImplId(impl_id) => {
+                let impl_substs = TyBuilder::subst_for_def(self.db, impl_id, None)
+                    .fill_with_inference_vars(&mut self.table)
+                    .build();
+                let impl_self_ty = self.db.impl_self_ty(impl_id).substitute(Interner, &impl_substs);
+                self.unify(&impl_self_ty, &ty);
+                impl_substs
             }
+            ItemContainerId::TraitId(trait_) => {
+                // we're picking this method
+                let trait_ref = TyBuilder::trait_ref(self.db, trait_)
+                    .push(ty.clone())
+                    .fill_with_inference_vars(&mut self.table)
+                    .build();
+                self.push_obligation(trait_ref.clone().cast(Interner));
+                trait_ref.substitution
+            }
+            ItemContainerId::ModuleId(_) | ItemContainerId::ExternBlockId(_) => {
+                never!("assoc item contained in module/extern block");
+                return None;
+            }
+        };
+
+        self.write_assoc_resolution(id, item, substs.clone());
+        if !visible {
+            self.push_diagnostic(InferenceDiagnostic::PrivateAssocItem { id, item });
         }
-        res.map(|(def, _, substs, _)| (def, substs))
+        Some((def, substs))
     }
 
     fn resolve_enum_variant_on_ty(
@@ -304,7 +287,7 @@ impl<'a> InferenceContext<'a> {
         ty: &Ty,
         name: &Name,
         id: ExprOrPatId,
-    ) -> Option<(ValueNs, Option<Substitution>)> {
+    ) -> Option<(ValueNs, Substitution)> {
         let ty = self.resolve_ty_shallow(ty);
         let (enum_id, subst) = match ty.as_adt() {
             Some((AdtId::EnumId(e), subst)) => (e, subst),
@@ -314,6 +297,6 @@ impl<'a> InferenceContext<'a> {
         let local_id = enum_data.variant(name)?;
         let variant = EnumVariantId { parent: enum_id, local_id };
         self.write_variant_resolution(id, variant.into());
-        Some((ValueNs::EnumVariantId(variant), Some(subst.clone())))
+        Some((ValueNs::EnumVariantId(variant), subst.clone()))
     }
 }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs
index 46ed3533c8c..504f0743aa9 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs
@@ -704,14 +704,13 @@ impl<'a> fmt::Debug for InferenceTable<'a> {
 mod resolve {
     use super::InferenceTable;
     use crate::{
-        ConcreteConst, Const, ConstData, ConstValue, DebruijnIndex, GenericArg, InferenceVar,
-        Interner, Lifetime, Ty, TyVariableKind, VariableKind,
+        ConcreteConst, Const, ConstData, ConstScalar, ConstValue, DebruijnIndex, GenericArg,
+        InferenceVar, Interner, Lifetime, Ty, TyVariableKind, VariableKind,
     };
     use chalk_ir::{
         cast::Cast,
         fold::{TypeFoldable, TypeFolder},
     };
-    use hir_def::type_ref::ConstScalar;
 
     #[derive(chalk_derive::FallibleTypeFolder)]
     #[has_interner(Interner)]
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs b/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs
index 0c547192ac0..36af78153d4 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs
@@ -6,12 +6,12 @@ use chalk_ir::{
     DebruijnIndex,
 };
 use hir_def::{
-    adt::VariantData, attr::Attrs, type_ref::ConstScalar, visibility::Visibility, AdtId,
-    EnumVariantId, HasModule, Lookup, ModuleId, VariantId,
+    adt::VariantData, attr::Attrs, visibility::Visibility, AdtId, EnumVariantId, HasModule, Lookup,
+    ModuleId, VariantId,
 };
 
 use crate::{
-    db::HirDatabase, Binders, ConcreteConst, Const, ConstValue, Interner, Substitution, Ty, TyKind,
+    consteval::try_const_usize, db::HirDatabase, Binders, Interner, Substitution, Ty, TyKind,
 };
 
 /// Checks whether a type is visibly uninhabited from a particular module.
@@ -69,7 +69,7 @@ impl TypeVisitor<Interner> for UninhabitedFrom<'_> {
             TyKind::Adt(adt, subst) => self.visit_adt(adt.0, subst),
             TyKind::Never => BREAK_VISIBLY_UNINHABITED,
             TyKind::Tuple(..) => ty.super_visit_with(self, outer_binder),
-            TyKind::Array(item_ty, len) => match try_usize_const(len) {
+            TyKind::Array(item_ty, len) => match try_const_usize(len) {
                 Some(0) | None => CONTINUE_OPAQUELY_INHABITED,
                 Some(1..) => item_ty.super_visit_with(self, outer_binder),
             },
@@ -160,14 +160,3 @@ impl UninhabitedFrom<'_> {
         }
     }
 }
-
-fn try_usize_const(c: &Const) -> Option<u128> {
-    let data = &c.data(Interner);
-    if data.ty.kind(Interner) != &TyKind::Scalar(chalk_ir::Scalar::Uint(chalk_ir::UintTy::Usize)) {
-        return None;
-    }
-    match data.value {
-        ConstValue::Concrete(ConcreteConst { interned: ConstScalar::UInt(value) }) => Some(value),
-        _ => None,
-    }
-}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/interner.rs b/src/tools/rust-analyzer/crates/hir-ty/src/interner.rs
index 7bf73560cbe..aea7e9762fd 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/interner.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/interner.rs
@@ -1,10 +1,10 @@
 //! Implementation of the Chalk `Interner` trait, which allows customizing the
 //! representation of the various objects Chalk deals with (types, goals etc.).
 
-use crate::{chalk_db, tls, GenericArg};
+use crate::{chalk_db, tls, ConstScalar, GenericArg};
 use base_db::salsa::InternId;
 use chalk_ir::{Goal, GoalData};
-use hir_def::{type_ref::ConstScalar, TypeAliasId};
+use hir_def::TypeAliasId;
 use intern::{impl_internable, Interned};
 use smallvec::SmallVec;
 use std::{fmt, sync::Arc};
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs
index f21b4f84c4c..b95bb01fcef 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs
@@ -11,7 +11,7 @@ use hir_def::{
 };
 use stdx::never;
 
-use crate::{db::HirDatabase, Interner, Substitution, Ty};
+use crate::{consteval::try_const_usize, db::HirDatabase, Interner, Substitution, Ty};
 
 use self::adt::struct_variant_idx;
 pub use self::{
@@ -122,17 +122,9 @@ pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty, krate: CrateId) -> Result<Lay
             cx.univariant(dl, &fields, &ReprOptions::default(), kind).ok_or(LayoutError::Unknown)?
         }
         TyKind::Array(element, count) => {
-            let count = match count.data(Interner).value {
-                chalk_ir::ConstValue::Concrete(c) => match c.interned {
-                    hir_def::type_ref::ConstScalar::Int(x) => x as u64,
-                    hir_def::type_ref::ConstScalar::UInt(x) => x as u64,
-                    hir_def::type_ref::ConstScalar::Unknown => {
-                        user_error!("unknown const generic parameter")
-                    }
-                    _ => user_error!("mismatched type of const generic parameter"),
-                },
-                _ => return Err(LayoutError::HasPlaceholder),
-            };
+            let count = try_const_usize(&count).ok_or(LayoutError::UserError(
+                "mismatched type of const generic parameter".to_string(),
+            ))? as u64;
             let element = layout_of_ty(db, element, krate)?;
             let size = element.size.checked_mul(count, dl).ok_or(LayoutError::SizeOverflow)?;
 
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs
index cb7968c1446..b22d0fe8ded 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs
@@ -76,17 +76,8 @@ pub fn layout_of_adt_query(
             |min, max| Integer::repr_discr(&dl, &repr, min, max).unwrap_or((Integer::I8, false)),
             variants.iter_enumerated().filter_map(|(id, _)| {
                 let AdtId::EnumId(e) = def else { return None };
-                let d = match db
-                    .const_eval_variant(EnumVariantId { parent: e, local_id: id.0 })
-                    .ok()?
-                {
-                    crate::consteval::ComputedExpr::Literal(l) => match l {
-                        hir_def::expr::Literal::Int(i, _) => i,
-                        hir_def::expr::Literal::Uint(i, _) => i as i128,
-                        _ => return None,
-                    },
-                    _ => return None,
-                };
+                let d =
+                    db.const_eval_discriminant(EnumVariantId { parent: e, local_id: id.0 }).ok()?;
                 Some((id, d))
             }),
             // FIXME: The current code for niche-filling relies on variant indices
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs
index 067bdc960da..a8971fde3c2 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs
@@ -65,25 +65,17 @@ fn eval_expr(ra_fixture: &str, minicore: &str) -> Result<Layout, LayoutError> {
         })
         .unwrap();
     let hir_body = db.body(adt_id.into());
-    let pat = hir_body
-        .pats
-        .iter()
-        .find(|x| match x.1 {
-            hir_def::expr::Pat::Bind { name, .. } => name.to_smol_str() == "goal",
-            _ => false,
-        })
-        .unwrap()
-        .0;
+    let b = hir_body.bindings.iter().find(|x| x.1.name.to_smol_str() == "goal").unwrap().0;
     let infer = db.infer(adt_id.into());
-    let goal_ty = infer.type_of_pat[pat].clone();
+    let goal_ty = infer.type_of_binding[b].clone();
     layout_of_ty(&db, &goal_ty, module_id.krate())
 }
 
 #[track_caller]
 fn check_size_and_align(ra_fixture: &str, minicore: &str, size: u64, align: u64) {
     let l = eval_goal(ra_fixture, minicore).unwrap();
-    assert_eq!(l.size.bytes(), size);
-    assert_eq!(l.align.abi.bytes(), align);
+    assert_eq!(l.size.bytes(), size, "size mismatch");
+    assert_eq!(l.align.abi.bytes(), align, "align mismatch");
 }
 
 #[track_caller]
@@ -300,4 +292,9 @@ fn enums_with_discriminants() {
             C, // implicitly becomes 256, so we need two bytes
         }
     }
+    size_and_align! {
+        enum Goal {
+            A = 1, // This one is (perhaps surprisingly) zero sized.
+        }
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs
index 59a5ef8c14d..9c63d67ab19 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs
@@ -13,6 +13,7 @@ mod builder;
 mod chalk_db;
 mod chalk_ext;
 pub mod consteval;
+pub mod mir;
 mod infer;
 mod inhabitedness;
 mod interner;
@@ -34,7 +35,7 @@ mod tests;
 #[cfg(test)]
 mod test_db;
 
-use std::sync::Arc;
+use std::{collections::HashMap, hash::Hash, sync::Arc};
 
 use chalk_ir::{
     fold::{Shift, TypeFoldable},
@@ -42,10 +43,11 @@ use chalk_ir::{
     visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor},
     NoSolution, TyData,
 };
+use either::Either;
 use hir_def::{expr::ExprId, type_ref::Rawness, TypeOrConstParamId};
 use hir_expand::name;
-use itertools::Either;
 use la_arena::{Arena, Idx};
+use mir::MirEvalError;
 use rustc_hash::FxHashSet;
 use traits::FnTrait;
 use utils::Generics;
@@ -145,6 +147,49 @@ pub type ConstrainedSubst = chalk_ir::ConstrainedSubst<Interner>;
 pub type Guidance = chalk_solve::Guidance<Interner>;
 pub type WhereClause = chalk_ir::WhereClause<Interner>;
 
+/// A constant can have reference to other things. Memory map job is holding
+/// the neccessary bits of memory of the const eval session to keep the constant
+/// meaningful.
+#[derive(Debug, Default, Clone, PartialEq, Eq)]
+pub struct MemoryMap(pub HashMap<usize, Vec<u8>>);
+
+impl MemoryMap {
+    fn insert(&mut self, addr: usize, x: Vec<u8>) {
+        self.0.insert(addr, x);
+    }
+
+    /// This functions convert each address by a function `f` which gets the byte intervals and assign an address
+    /// to them. It is useful when you want to load a constant with a memory map in a new memory. You can pass an
+    /// allocator function as `f` and it will return a mapping of old addresses to new addresses.
+    fn transform_addresses(
+        &self,
+        mut f: impl FnMut(&[u8]) -> Result<usize, MirEvalError>,
+    ) -> Result<HashMap<usize, usize>, MirEvalError> {
+        self.0.iter().map(|x| Ok((*x.0, f(x.1)?))).collect()
+    }
+}
+
+/// A concrete constant value
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum ConstScalar {
+    Bytes(Vec<u8>, MemoryMap),
+    /// Case of an unknown value that rustc might know but we don't
+    // FIXME: this is a hack to get around chalk not being able to represent unevaluatable
+    // constants
+    // https://github.com/rust-lang/rust-analyzer/pull/8813#issuecomment-840679177
+    // https://rust-lang.zulipchat.com/#narrow/stream/144729-wg-traits/topic/Handling.20non.20evaluatable.20constants'.20equality/near/238386348
+    Unknown,
+}
+
+impl Hash for ConstScalar {
+    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
+        core::mem::discriminant(self).hash(state);
+        if let ConstScalar::Bytes(b, _) = self {
+            b.hash(state)
+        }
+    }
+}
+
 /// Return an index of a parameter in the generic type parameter list by it's id.
 pub fn param_idx(db: &dyn HirDatabase, id: TypeOrConstParamId) -> Option<usize> {
     generics(db.upcast(), id.parent).param_idx(id)
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs
index 29964673722..23b15087e31 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs
@@ -16,6 +16,7 @@ use chalk_ir::{
     cast::Cast, fold::Shift, fold::TypeFoldable, interner::HasInterner, Mutability, Safety,
 };
 
+use either::Either;
 use hir_def::{
     adt::StructKind,
     body::{Expander, LowerCtx},
@@ -26,16 +27,13 @@ use hir_def::{
     lang_item::{lang_attr, LangItem},
     path::{GenericArg, ModPath, Path, PathKind, PathSegment, PathSegments},
     resolver::{HasResolver, Resolver, TypeNs},
-    type_ref::{
-        ConstScalarOrPath, TraitBoundModifier, TraitRef as HirTraitRef, TypeBound, TypeRef,
-    },
+    type_ref::{ConstRefOrPath, TraitBoundModifier, TraitRef as HirTraitRef, TypeBound, TypeRef},
     AdtId, AssocItemId, ConstId, ConstParamId, EnumId, EnumVariantId, FunctionId, GenericDefId,
     HasModule, ImplId, ItemContainerId, LocalFieldId, Lookup, ModuleDefId, StaticId, StructId,
     TraitId, TypeAliasId, TypeOrConstParamId, TypeParamId, UnionId, VariantId,
 };
 use hir_expand::{name::Name, ExpandResult};
 use intern::Interned;
-use itertools::Either;
 use la_arena::{Arena, ArenaMap};
 use rustc_hash::FxHashSet;
 use smallvec::SmallVec;
@@ -44,7 +42,7 @@ use syntax::ast;
 
 use crate::{
     all_super_traits,
-    consteval::{intern_const_scalar, path_to_const, unknown_const, unknown_const_as_generic},
+    consteval::{intern_const_ref, path_to_const, unknown_const, unknown_const_as_generic},
     db::HirDatabase,
     make_binders,
     mapping::{from_chalk_trait_id, ToChalk},
@@ -524,6 +522,10 @@ impl<'a> TyLoweringContext<'a> {
                 };
                 return (ty, None);
             }
+            TypeNs::TraitAliasId(_) => {
+                // FIXME(trait_alias): Implement trait alias.
+                return (TyKind::Error.intern(Interner), None);
+            }
             TypeNs::GenericParam(param_id) => {
                 let generics = generics(
                     self.db.upcast(),
@@ -879,6 +881,7 @@ impl<'a> TyLoweringContext<'a> {
     ) -> Option<TraitRef> {
         let resolved =
             match self.resolver.resolve_path_in_type_ns_fully(self.db.upcast(), path.mod_path())? {
+                // FIXME(trait_alias): We need to handle trait alias here.
                 TypeNs::TraitId(tr) => tr,
                 _ => return None,
             };
@@ -968,7 +971,7 @@ impl<'a> TyLoweringContext<'a> {
                         // - `Destruct` impls are built-in in 1.62 (current nightlies as of 08-04-2022), so until
                         //   the builtin impls are supported by Chalk, we ignore them here.
                         if let Some(lang) = lang_attr(self.db.upcast(), tr.hir_trait_id()) {
-                            if lang == "drop" || lang == "destruct" {
+                            if matches!(lang, LangItem::Drop | LangItem::Destruct) {
                                 return false;
                             }
                         }
@@ -1444,6 +1447,7 @@ pub(crate) fn trait_environment_query(
         GenericDefId::FunctionId(f) => Some(f.lookup(db.upcast()).container),
         GenericDefId::AdtId(_) => None,
         GenericDefId::TraitId(_) => None,
+        GenericDefId::TraitAliasId(_) => None,
         GenericDefId::TypeAliasId(t) => Some(t.lookup(db.upcast()).container),
         GenericDefId::ImplId(_) => None,
         GenericDefId::EnumVariantId(_) => None,
@@ -1583,10 +1587,10 @@ pub(crate) fn generic_defaults_recover(
         .iter_id()
         .map(|id| {
             let val = match id {
-                itertools::Either::Left(_) => {
+                Either::Left(_) => {
                     GenericArgData::Ty(TyKind::Error.intern(Interner)).intern(Interner)
                 }
-                itertools::Either::Right(id) => unknown_const_as_generic(db.const_param_ty(id)),
+                Either::Right(id) => unknown_const_as_generic(db.const_param_ty(id)),
             };
             crate::make_binders(db, &generic_params, val)
         })
@@ -1919,7 +1923,7 @@ pub(crate) fn generic_arg_to_chalk<'a, T>(
     arg: &'a GenericArg,
     this: &mut T,
     for_type: impl FnOnce(&mut T, &TypeRef) -> Ty + 'a,
-    for_const: impl FnOnce(&mut T, &ConstScalarOrPath, Ty) -> Const + 'a,
+    for_const: impl FnOnce(&mut T, &ConstRefOrPath, Ty) -> Const + 'a,
 ) -> Option<crate::GenericArg> {
     let kind = match kind_id {
         Either::Left(_) => ParamKind::Type,
@@ -1947,7 +1951,7 @@ pub(crate) fn generic_arg_to_chalk<'a, T>(
                 let p = p.mod_path();
                 if p.kind == PathKind::Plain {
                     if let [n] = p.segments() {
-                        let c = ConstScalarOrPath::Path(n.clone());
+                        let c = ConstRefOrPath::Path(n.clone());
                         return Some(
                             GenericArgData::Const(for_const(this, &c, c_ty)).intern(Interner),
                         );
@@ -1964,14 +1968,14 @@ pub(crate) fn const_or_path_to_chalk(
     db: &dyn HirDatabase,
     resolver: &Resolver,
     expected_ty: Ty,
-    value: &ConstScalarOrPath,
+    value: &ConstRefOrPath,
     mode: ParamLoweringMode,
     args: impl FnOnce() -> Generics,
     debruijn: DebruijnIndex,
 ) -> Const {
     match value {
-        ConstScalarOrPath::Scalar(s) => intern_const_scalar(*s, expected_ty),
-        ConstScalarOrPath::Path(n) => {
+        ConstRefOrPath::Scalar(s) => intern_const_ref(db, s, expected_ty, resolver.krate()),
+        ConstRefOrPath::Path(n) => {
             let path = ModPath::from_segments(PathKind::Plain, Some(n.clone()));
             path_to_const(db, resolver, &path, mode, args, debruijn)
                 .unwrap_or_else(|| unknown_const(expected_ty))
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs
index 8c7714b9a69..92a17fc3a99 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs
@@ -579,8 +579,8 @@ impl ReceiverAdjustments {
                     ty = new_ty.clone();
                     adjust.push(Adjustment {
                         kind: Adjust::Deref(match kind {
-                            // FIXME should we know the mutability here?
-                            AutoderefKind::Overloaded => Some(OverloadedDeref(Mutability::Not)),
+                            // FIXME should we know the mutability here, when autoref is `None`?
+                            AutoderefKind::Overloaded => Some(OverloadedDeref(self.autoref)),
                             AutoderefKind::Builtin => None,
                         }),
                         target: new_ty,
@@ -660,10 +660,10 @@ pub fn lookup_impl_const(
     env: Arc<TraitEnvironment>,
     const_id: ConstId,
     subs: Substitution,
-) -> ConstId {
+) -> (ConstId, Substitution) {
     let trait_id = match const_id.lookup(db.upcast()).container {
         ItemContainerId::TraitId(id) => id,
-        _ => return const_id,
+        _ => return (const_id, subs),
     };
     let substitution = Substitution::from_iter(Interner, subs.iter(Interner));
     let trait_ref = TraitRef { trait_id: to_chalk_trait_id(trait_id), substitution };
@@ -671,12 +671,14 @@ pub fn lookup_impl_const(
     let const_data = db.const_data(const_id);
     let name = match const_data.name.as_ref() {
         Some(name) => name,
-        None => return const_id,
+        None => return (const_id, subs),
     };
 
     lookup_impl_assoc_item_for_trait_ref(trait_ref, db, env, name)
-        .and_then(|assoc| if let AssocItemId::ConstId(id) = assoc { Some(id) } else { None })
-        .unwrap_or(const_id)
+        .and_then(
+            |assoc| if let (AssocItemId::ConstId(id), s) = assoc { Some((id, s)) } else { None },
+        )
+        .unwrap_or((const_id, subs))
 }
 
 /// Looks up the impl method that actually runs for the trait method `func`.
@@ -687,10 +689,10 @@ pub fn lookup_impl_method(
     env: Arc<TraitEnvironment>,
     func: FunctionId,
     fn_subst: Substitution,
-) -> FunctionId {
+) -> (FunctionId, Substitution) {
     let trait_id = match func.lookup(db.upcast()).container {
         ItemContainerId::TraitId(id) => id,
-        _ => return func,
+        _ => return (func, fn_subst),
     };
     let trait_params = db.generic_params(trait_id.into()).type_or_consts.len();
     let fn_params = fn_subst.len(Interner) - trait_params;
@@ -701,8 +703,14 @@ pub fn lookup_impl_method(
 
     let name = &db.function_data(func).name;
     lookup_impl_assoc_item_for_trait_ref(trait_ref, db, env, name)
-        .and_then(|assoc| if let AssocItemId::FunctionId(id) = assoc { Some(id) } else { None })
-        .unwrap_or(func)
+        .and_then(|assoc| {
+            if let (AssocItemId::FunctionId(id), subst) = assoc {
+                Some((id, subst))
+            } else {
+                None
+            }
+        })
+        .unwrap_or((func, fn_subst))
 }
 
 fn lookup_impl_assoc_item_for_trait_ref(
@@ -710,7 +718,7 @@ fn lookup_impl_assoc_item_for_trait_ref(
     db: &dyn HirDatabase,
     env: Arc<TraitEnvironment>,
     name: &Name,
-) -> Option<AssocItemId> {
+) -> Option<(AssocItemId, Substitution)> {
     let self_ty = trait_ref.self_type_parameter(Interner);
     let self_ty_fp = TyFingerprint::for_trait_impl(&self_ty)?;
     let impls = db.trait_impls_in_deps(env.krate);
@@ -718,8 +726,8 @@ fn lookup_impl_assoc_item_for_trait_ref(
 
     let table = InferenceTable::new(db, env);
 
-    let impl_data = find_matching_impl(impls, table, trait_ref)?;
-    impl_data.items.iter().find_map(|&it| match it {
+    let (impl_data, impl_subst) = find_matching_impl(impls, table, trait_ref)?;
+    let item = impl_data.items.iter().find_map(|&it| match it {
         AssocItemId::FunctionId(f) => {
             (db.function_data(f).name == *name).then_some(AssocItemId::FunctionId(f))
         }
@@ -730,14 +738,15 @@ fn lookup_impl_assoc_item_for_trait_ref(
             .map(|n| n == name)
             .and_then(|result| if result { Some(AssocItemId::ConstId(c)) } else { None }),
         AssocItemId::TypeAliasId(_) => None,
-    })
+    })?;
+    Some((item, impl_subst))
 }
 
 fn find_matching_impl(
     mut impls: impl Iterator<Item = ImplId>,
     mut table: InferenceTable<'_>,
     actual_trait_ref: TraitRef,
-) -> Option<Arc<ImplData>> {
+) -> Option<(Arc<ImplData>, Substitution)> {
     let db = table.db;
     loop {
         let impl_ = impls.next()?;
@@ -758,7 +767,7 @@ fn find_matching_impl(
                 .into_iter()
                 .map(|b| b.cast(Interner));
             let goal = crate::Goal::all(Interner, wcs);
-            table.try_obligation(goal).map(|_| impl_data)
+            table.try_obligation(goal).map(|_| (impl_data, table.resolve_completely(impl_substs)))
         });
         if r.is_some() {
             break r;
@@ -821,9 +830,9 @@ 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_chain = autoderef_method_receiver(&mut table, ty);
 
-            let result = deref_chain.into_iter().zip(adj).try_for_each(|(receiver_ty, adj)| {
+            let result = deref_chain.into_iter().try_for_each(|(receiver_ty, adj)| {
                 iterate_method_candidates_with_autoref(
                     &receiver_ty,
                     adj,
@@ -867,16 +876,20 @@ fn iterate_method_candidates_with_autoref(
         return ControlFlow::Continue(());
     }
 
-    iterate_method_candidates_by_receiver(
-        receiver_ty,
-        first_adjustment.clone(),
-        db,
-        env.clone(),
-        traits_in_scope,
-        visible_from_module,
-        name,
-        &mut callback,
-    )?;
+    let mut iterate_method_candidates_by_receiver = move |receiver_ty, first_adjustment| {
+        iterate_method_candidates_by_receiver(
+            receiver_ty,
+            first_adjustment,
+            db,
+            env.clone(),
+            traits_in_scope,
+            visible_from_module,
+            name,
+            &mut callback,
+        )
+    };
+
+    iterate_method_candidates_by_receiver(receiver_ty, first_adjustment.clone())?;
 
     let refed = Canonical {
         value: TyKind::Ref(Mutability::Not, static_lifetime(), receiver_ty.value.clone())
@@ -884,16 +897,7 @@ fn iterate_method_candidates_with_autoref(
         binders: receiver_ty.binders.clone(),
     };
 
-    iterate_method_candidates_by_receiver(
-        &refed,
-        first_adjustment.with_autoref(Mutability::Not),
-        db,
-        env.clone(),
-        traits_in_scope,
-        visible_from_module,
-        name,
-        &mut callback,
-    )?;
+    iterate_method_candidates_by_receiver(&refed, first_adjustment.with_autoref(Mutability::Not))?;
 
     let ref_muted = Canonical {
         value: TyKind::Ref(Mutability::Mut, static_lifetime(), receiver_ty.value.clone())
@@ -904,12 +908,6 @@ fn iterate_method_candidates_with_autoref(
     iterate_method_candidates_by_receiver(
         &ref_muted,
         first_adjustment.with_autoref(Mutability::Mut),
-        db,
-        env,
-        traits_in_scope,
-        visible_from_module,
-        name,
-        &mut callback,
     )
 }
 
@@ -1210,8 +1208,8 @@ pub fn resolve_indexing_op(
 ) -> Option<ReceiverAdjustments> {
     let mut table = InferenceTable::new(db, env.clone());
     let ty = table.instantiate_canonical(ty);
-    let (deref_chain, adj) = autoderef_method_receiver(&mut table, ty);
-    for (ty, adj) in deref_chain.into_iter().zip(adj) {
+    let deref_chain = autoderef_method_receiver(&mut table, ty);
+    for (ty, adj) in deref_chain {
         let goal = generic_implements_goal(db, env.clone(), index_trait, &ty);
         if db.trait_solve(env.krate, goal.cast(Interner)).is_some() {
             return Some(adj);
@@ -1421,25 +1419,24 @@ fn generic_implements_goal(
 fn autoderef_method_receiver(
     table: &mut InferenceTable<'_>,
     ty: Ty,
-) -> (Vec<Canonical<Ty>>, Vec<ReceiverAdjustments>) {
-    let (mut deref_chain, mut adjustments): (Vec<_>, Vec<_>) = (Vec::new(), Vec::new());
+) -> Vec<(Canonical<Ty>, ReceiverAdjustments)> {
+    let mut deref_chain: Vec<_> = Vec::new();
     let mut autoderef = autoderef::Autoderef::new(table, ty);
     while let Some((ty, derefs)) = autoderef.next() {
-        deref_chain.push(autoderef.table.canonicalize(ty).value);
-        adjustments.push(ReceiverAdjustments {
-            autoref: None,
-            autoderefs: derefs,
-            unsize_array: false,
-        });
+        deref_chain.push((
+            autoderef.table.canonicalize(ty).value,
+            ReceiverAdjustments { autoref: None, autoderefs: derefs, unsize_array: false },
+        ));
     }
     // As a last step, we can do array unsizing (that's the only unsizing that rustc does for method receivers!)
-    if let (Some((TyKind::Array(parameters, _), binders)), Some(adj)) = (
-        deref_chain.last().map(|ty| (ty.value.kind(Interner), ty.binders.clone())),
-        adjustments.last().cloned(),
-    ) {
+    if let Some((TyKind::Array(parameters, _), binders, adj)) =
+        deref_chain.last().map(|(ty, adj)| (ty.value.kind(Interner), ty.binders.clone(), adj))
+    {
         let unsized_ty = TyKind::Slice(parameters.clone()).intern(Interner);
-        deref_chain.push(Canonical { value: unsized_ty, binders });
-        adjustments.push(ReceiverAdjustments { unsize_array: true, ..adj });
+        deref_chain.push((
+            Canonical { value: unsized_ty, binders },
+            ReceiverAdjustments { unsize_array: true, ..adj.clone() },
+        ));
     }
-    (deref_chain, adjustments)
+    deref_chain
 }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs
new file mode 100644
index 00000000000..7c1cbbdf53d
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs
@@ -0,0 +1,863 @@
+//! MIR definitions and implementation
+
+use std::{fmt::Display, iter};
+
+use crate::{
+    infer::PointerCast, Const, ConstScalar, InferenceResult, Interner, MemoryMap, Substitution, Ty,
+};
+use chalk_ir::Mutability;
+use hir_def::{
+    expr::{BindingId, Expr, ExprId, Ordering, PatId},
+    DefWithBodyId, FieldId, UnionId, VariantId,
+};
+use la_arena::{Arena, ArenaMap, Idx, RawIdx};
+
+mod eval;
+mod lower;
+mod borrowck;
+mod pretty;
+
+pub use borrowck::{borrowck_query, BorrowckResult, MutabilityReason};
+pub use eval::{interpret_mir, pad16, Evaluator, MirEvalError};
+pub use lower::{lower_to_mir, mir_body_query, mir_body_recover, MirLowerError};
+use smallvec::{smallvec, SmallVec};
+use stdx::impl_from;
+
+use super::consteval::{intern_const_scalar, try_const_usize};
+
+pub type BasicBlockId = Idx<BasicBlock>;
+pub type LocalId = Idx<Local>;
+
+fn return_slot() -> LocalId {
+    LocalId::from_raw(RawIdx::from(0))
+}
+
+#[derive(Debug, PartialEq, Eq)]
+pub struct Local {
+    pub ty: Ty,
+}
+
+/// An operand in MIR represents a "value" in Rust, the definition of which is undecided and part of
+/// the memory model. One proposal for a definition of values can be found [on UCG][value-def].
+///
+/// [value-def]: https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/value-domain.md
+///
+/// The most common way to create values is via loading a place. Loading a place is an operation
+/// which reads the memory of the place and converts it to a value. This is a fundamentally *typed*
+/// operation. The nature of the value produced depends on the type of the conversion. Furthermore,
+/// there may be other effects: if the type has a validity constraint loading the place might be UB
+/// if the validity constraint is not met.
+///
+/// **Needs clarification:** Ralf proposes that loading a place not have side-effects.
+/// This is what is implemented in miri today. Are these the semantics we want for MIR? Is this
+/// something we can even decide without knowing more about Rust's memory model?
+///
+/// **Needs clarifiation:** Is loading a place that has its variant index set well-formed? Miri
+/// currently implements it, but it seems like this may be something to check against in the
+/// validator.
+#[derive(Debug, PartialEq, Eq, Clone)]
+pub enum Operand {
+    /// Creates a value by loading the given place.
+    ///
+    /// Before drop elaboration, the type of the place must be `Copy`. After drop elaboration there
+    /// is no such requirement.
+    Copy(Place),
+
+    /// Creates a value by performing loading the place, just like the `Copy` operand.
+    ///
+    /// This *may* additionally overwrite the place with `uninit` bytes, depending on how we decide
+    /// in [UCG#188]. You should not emit MIR that may attempt a subsequent second load of this
+    /// place without first re-initializing it.
+    ///
+    /// [UCG#188]: https://github.com/rust-lang/unsafe-code-guidelines/issues/188
+    Move(Place),
+    /// Constants are already semantically values, and remain unchanged.
+    Constant(Const),
+}
+
+impl Operand {
+    fn from_concrete_const(data: Vec<u8>, memory_map: MemoryMap, ty: Ty) -> Self {
+        Operand::Constant(intern_const_scalar(ConstScalar::Bytes(data, memory_map), ty))
+    }
+
+    fn from_bytes(data: Vec<u8>, ty: Ty) -> Self {
+        Operand::from_concrete_const(data, MemoryMap::default(), ty)
+    }
+
+    fn const_zst(ty: Ty) -> Operand {
+        Self::from_bytes(vec![], ty)
+    }
+}
+
+#[derive(Debug, PartialEq, Eq, Clone)]
+pub enum ProjectionElem<V, T> {
+    Deref,
+    Field(FieldId),
+    TupleField(usize),
+    Index(V),
+    ConstantIndex { offset: u64, min_length: u64, from_end: bool },
+    Subslice { from: u64, to: u64, from_end: bool },
+    //Downcast(Option<Symbol>, VariantIdx),
+    OpaqueCast(T),
+}
+
+type PlaceElem = ProjectionElem<LocalId, Ty>;
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct Place {
+    pub local: LocalId,
+    pub projection: Vec<PlaceElem>,
+}
+
+impl From<LocalId> for Place {
+    fn from(local: LocalId) -> Self {
+        Self { local, projection: vec![] }
+    }
+}
+
+#[derive(Debug, PartialEq, Eq, Clone)]
+pub enum AggregateKind {
+    /// The type is of the element
+    Array(Ty),
+    /// The type is of the tuple
+    Tuple(Ty),
+    Adt(VariantId, Substitution),
+    Union(UnionId, FieldId),
+    //Closure(LocalDefId, SubstsRef),
+    //Generator(LocalDefId, SubstsRef, Movability),
+}
+
+#[derive(Debug, Clone, Hash, PartialEq, Eq)]
+pub struct SwitchTargets {
+    /// Possible values. The locations to branch to in each case
+    /// are found in the corresponding indices from the `targets` vector.
+    values: SmallVec<[u128; 1]>,
+
+    /// Possible branch sites. The last element of this vector is used
+    /// for the otherwise branch, so targets.len() == values.len() + 1
+    /// should hold.
+    //
+    // This invariant is quite non-obvious and also could be improved.
+    // One way to make this invariant is to have something like this instead:
+    //
+    // branches: Vec<(ConstInt, BasicBlock)>,
+    // otherwise: Option<BasicBlock> // exhaustive if None
+    //
+    // However we’ve decided to keep this as-is until we figure a case
+    // where some other approach seems to be strictly better than other.
+    targets: SmallVec<[BasicBlockId; 2]>,
+}
+
+impl SwitchTargets {
+    /// Creates switch targets from an iterator of values and target blocks.
+    ///
+    /// The iterator may be empty, in which case the `SwitchInt` instruction is equivalent to
+    /// `goto otherwise;`.
+    pub fn new(
+        targets: impl Iterator<Item = (u128, BasicBlockId)>,
+        otherwise: BasicBlockId,
+    ) -> Self {
+        let (values, mut targets): (SmallVec<_>, SmallVec<_>) = targets.unzip();
+        targets.push(otherwise);
+        Self { values, targets }
+    }
+
+    /// Builds a switch targets definition that jumps to `then` if the tested value equals `value`,
+    /// and to `else_` if not.
+    pub fn static_if(value: u128, then: BasicBlockId, else_: BasicBlockId) -> Self {
+        Self { values: smallvec![value], targets: smallvec![then, else_] }
+    }
+
+    /// Returns the fallback target that is jumped to when none of the values match the operand.
+    pub fn otherwise(&self) -> BasicBlockId {
+        *self.targets.last().unwrap()
+    }
+
+    /// Returns an iterator over the switch targets.
+    ///
+    /// The iterator will yield tuples containing the value and corresponding target to jump to, not
+    /// including the `otherwise` fallback target.
+    ///
+    /// Note that this may yield 0 elements. Only the `otherwise` branch is mandatory.
+    pub fn iter(&self) -> impl Iterator<Item = (u128, BasicBlockId)> + '_ {
+        iter::zip(&self.values, &self.targets).map(|(x, y)| (*x, *y))
+    }
+
+    /// Returns a slice with all possible jump targets (including the fallback target).
+    pub fn all_targets(&self) -> &[BasicBlockId] {
+        &self.targets
+    }
+
+    /// Finds the `BasicBlock` to which this `SwitchInt` will branch given the
+    /// specific value. This cannot fail, as it'll return the `otherwise`
+    /// branch if there's not a specific match for the value.
+    pub fn target_for_value(&self, value: u128) -> BasicBlockId {
+        self.iter().find_map(|(v, t)| (v == value).then_some(t)).unwrap_or_else(|| self.otherwise())
+    }
+}
+
+#[derive(Debug, PartialEq, Eq, Clone)]
+pub enum Terminator {
+    /// Block has one successor; we continue execution there.
+    Goto { target: BasicBlockId },
+
+    /// Switches based on the computed value.
+    ///
+    /// First, evaluates the `discr` operand. The type of the operand must be a signed or unsigned
+    /// integer, char, or bool, and must match the given type. Then, if the list of switch targets
+    /// contains the computed value, continues execution at the associated basic block. Otherwise,
+    /// continues execution at the "otherwise" basic block.
+    ///
+    /// Target values may not appear more than once.
+    SwitchInt {
+        /// The discriminant value being tested.
+        discr: Operand,
+
+        targets: SwitchTargets,
+    },
+
+    /// Indicates that the landing pad is finished and that the process should continue unwinding.
+    ///
+    /// Like a return, this marks the end of this invocation of the function.
+    ///
+    /// Only permitted in cleanup blocks. `Resume` is not permitted with `-C unwind=abort` after
+    /// deaggregation runs.
+    Resume,
+
+    /// Indicates that the landing pad is finished and that the process should abort.
+    ///
+    /// Used to prevent unwinding for foreign items or with `-C unwind=abort`. Only permitted in
+    /// cleanup blocks.
+    Abort,
+
+    /// Returns from the function.
+    ///
+    /// Like function calls, the exact semantics of returns in Rust are unclear. Returning very
+    /// likely at least assigns the value currently in the return place (`_0`) to the place
+    /// specified in the associated `Call` terminator in the calling function, as if assigned via
+    /// `dest = move _0`. It might additionally do other things, like have side-effects in the
+    /// aliasing model.
+    ///
+    /// If the body is a generator body, this has slightly different semantics; it instead causes a
+    /// `GeneratorState::Returned(_0)` to be created (as if by an `Aggregate` rvalue) and assigned
+    /// to the return place.
+    Return,
+
+    /// Indicates a terminator that can never be reached.
+    ///
+    /// Executing this terminator is UB.
+    Unreachable,
+
+    /// The behavior of this statement differs significantly before and after drop elaboration.
+    /// After drop elaboration, `Drop` executes the drop glue for the specified place, after which
+    /// it continues execution/unwinds at the given basic blocks. It is possible that executing drop
+    /// glue is special - this would be part of Rust's memory model. (**FIXME**: due we have an
+    /// issue tracking if drop glue has any interesting semantics in addition to those of a function
+    /// call?)
+    ///
+    /// `Drop` before drop elaboration is a *conditional* execution of the drop glue. Specifically, the
+    /// `Drop` will be executed if...
+    ///
+    /// **Needs clarification**: End of that sentence. This in effect should document the exact
+    /// behavior of drop elaboration. The following sounds vaguely right, but I'm not quite sure:
+    ///
+    /// > The drop glue is executed if, among all statements executed within this `Body`, an assignment to
+    /// > the place or one of its "parents" occurred more recently than a move out of it. This does not
+    /// > consider indirect assignments.
+    Drop { place: Place, target: BasicBlockId, unwind: Option<BasicBlockId> },
+
+    /// Drops the place and assigns a new value to it.
+    ///
+    /// This first performs the exact same operation as the pre drop-elaboration `Drop` terminator;
+    /// it then additionally assigns the `value` to the `place` as if by an assignment statement.
+    /// This assignment occurs both in the unwind and the regular code paths. The semantics are best
+    /// explained by the elaboration:
+    ///
+    /// ```ignore (MIR)
+    /// BB0 {
+    ///   DropAndReplace(P <- V, goto BB1, unwind BB2)
+    /// }
+    /// ```
+    ///
+    /// becomes
+    ///
+    /// ```ignore (MIR)
+    /// BB0 {
+    ///   Drop(P, goto BB1, unwind BB2)
+    /// }
+    /// BB1 {
+    ///   // P is now uninitialized
+    ///   P <- V
+    /// }
+    /// BB2 {
+    ///   // P is now uninitialized -- its dtor panicked
+    ///   P <- V
+    /// }
+    /// ```
+    ///
+    /// Disallowed after drop elaboration.
+    DropAndReplace {
+        place: Place,
+        value: Operand,
+        target: BasicBlockId,
+        unwind: Option<BasicBlockId>,
+    },
+
+    /// Roughly speaking, evaluates the `func` operand and the arguments, and starts execution of
+    /// the referred to function. The operand types must match the argument types of the function.
+    /// The return place type must match the return type. The type of the `func` operand must be
+    /// callable, meaning either a function pointer, a function type, or a closure type.
+    ///
+    /// **Needs clarification**: The exact semantics of this. Current backends rely on `move`
+    /// operands not aliasing the return place. It is unclear how this is justified in MIR, see
+    /// [#71117].
+    ///
+    /// [#71117]: https://github.com/rust-lang/rust/issues/71117
+    Call {
+        /// The function that’s being called.
+        func: Operand,
+        /// Arguments the function is called with.
+        /// These are owned by the callee, which is free to modify them.
+        /// This allows the memory occupied by "by-value" arguments to be
+        /// reused across function calls without duplicating the contents.
+        args: Vec<Operand>,
+        /// Where the returned value will be written
+        destination: Place,
+        /// Where to go after this call returns. If none, the call necessarily diverges.
+        target: Option<BasicBlockId>,
+        /// Cleanups to be done if the call unwinds.
+        cleanup: Option<BasicBlockId>,
+        /// `true` if this is from a call in HIR rather than from an overloaded
+        /// operator. True for overloaded function call.
+        from_hir_call: bool,
+        // This `Span` is the span of the function, without the dot and receiver
+        // (e.g. `foo(a, b)` in `x.foo(a, b)`
+        //fn_span: Span,
+    },
+
+    /// Evaluates the operand, which must have type `bool`. If it is not equal to `expected`,
+    /// initiates a panic. Initiating a panic corresponds to a `Call` terminator with some
+    /// unspecified constant as the function to call, all the operands stored in the `AssertMessage`
+    /// as parameters, and `None` for the destination. Keep in mind that the `cleanup` path is not
+    /// necessarily executed even in the case of a panic, for example in `-C panic=abort`. If the
+    /// assertion does not fail, execution continues at the specified basic block.
+    Assert {
+        cond: Operand,
+        expected: bool,
+        //msg: AssertMessage,
+        target: BasicBlockId,
+        cleanup: Option<BasicBlockId>,
+    },
+
+    /// Marks a suspend point.
+    ///
+    /// Like `Return` terminators in generator bodies, this computes `value` and then a
+    /// `GeneratorState::Yielded(value)` as if by `Aggregate` rvalue. That value is then assigned to
+    /// the return place of the function calling this one, and execution continues in the calling
+    /// function. When next invoked with the same first argument, execution of this function
+    /// continues at the `resume` basic block, with the second argument written to the `resume_arg`
+    /// place. If the generator is dropped before then, the `drop` basic block is invoked.
+    ///
+    /// Not permitted in bodies that are not generator bodies, or after generator lowering.
+    ///
+    /// **Needs clarification**: What about the evaluation order of the `resume_arg` and `value`?
+    Yield {
+        /// The value to return.
+        value: Operand,
+        /// Where to resume to.
+        resume: BasicBlockId,
+        /// The place to store the resume argument in.
+        resume_arg: Place,
+        /// Cleanup to be done if the generator is dropped at this suspend point.
+        drop: Option<BasicBlockId>,
+    },
+
+    /// Indicates the end of dropping a generator.
+    ///
+    /// Semantically just a `return` (from the generators drop glue). Only permitted in the same situations
+    /// as `yield`.
+    ///
+    /// **Needs clarification**: Is that even correct? The generator drop code is always confusing
+    /// to me, because it's not even really in the current body.
+    ///
+    /// **Needs clarification**: Are there type system constraints on these terminators? Should
+    /// there be a "block type" like `cleanup` blocks for them?
+    GeneratorDrop,
+
+    /// A block where control flow only ever takes one real path, but borrowck needs to be more
+    /// conservative.
+    ///
+    /// At runtime this is semantically just a goto.
+    ///
+    /// Disallowed after drop elaboration.
+    FalseEdge {
+        /// The target normal control flow will take.
+        real_target: BasicBlockId,
+        /// A block control flow could conceptually jump to, but won't in
+        /// practice.
+        imaginary_target: BasicBlockId,
+    },
+
+    /// A terminator for blocks that only take one path in reality, but where we reserve the right
+    /// to unwind in borrowck, even if it won't happen in practice. This can arise in infinite loops
+    /// with no function calls for example.
+    ///
+    /// At runtime this is semantically just a goto.
+    ///
+    /// Disallowed after drop elaboration.
+    FalseUnwind {
+        /// The target normal control flow will take.
+        real_target: BasicBlockId,
+        /// The imaginary cleanup block link. This particular path will never be taken
+        /// in practice, but in order to avoid fragility we want to always
+        /// consider it in borrowck. We don't want to accept programs which
+        /// pass borrowck only when `panic=abort` or some assertions are disabled
+        /// due to release vs. debug mode builds. This needs to be an `Option` because
+        /// of the `remove_noop_landing_pads` and `abort_unwinding_calls` passes.
+        unwind: Option<BasicBlockId>,
+    },
+}
+
+#[derive(Debug, PartialEq, Eq, Clone)]
+pub enum BorrowKind {
+    /// Data must be immutable and is aliasable.
+    Shared,
+
+    /// The immediately borrowed place must be immutable, but projections from
+    /// it don't need to be. For example, a shallow borrow of `a.b` doesn't
+    /// conflict with a mutable borrow of `a.b.c`.
+    ///
+    /// This is used when lowering matches: when matching on a place we want to
+    /// ensure that place have the same value from the start of the match until
+    /// an arm is selected. This prevents this code from compiling:
+    /// ```compile_fail,E0510
+    /// let mut x = &Some(0);
+    /// match *x {
+    ///     None => (),
+    ///     Some(_) if { x = &None; false } => (),
+    ///     Some(_) => (),
+    /// }
+    /// ```
+    /// This can't be a shared borrow because mutably borrowing (*x as Some).0
+    /// should not prevent `if let None = x { ... }`, for example, because the
+    /// mutating `(*x as Some).0` can't affect the discriminant of `x`.
+    /// We can also report errors with this kind of borrow differently.
+    Shallow,
+
+    /// Data must be immutable but not aliasable. This kind of borrow
+    /// cannot currently be expressed by the user and is used only in
+    /// implicit closure bindings. It is needed when the closure is
+    /// borrowing or mutating a mutable referent, e.g.:
+    /// ```
+    /// let mut z = 3;
+    /// let x: &mut isize = &mut z;
+    /// let y = || *x += 5;
+    /// ```
+    /// If we were to try to translate this closure into a more explicit
+    /// form, we'd encounter an error with the code as written:
+    /// ```compile_fail,E0594
+    /// struct Env<'a> { x: &'a &'a mut isize }
+    /// let mut z = 3;
+    /// let x: &mut isize = &mut z;
+    /// let y = (&mut Env { x: &x }, fn_ptr);  // Closure is pair of env and fn
+    /// fn fn_ptr(env: &mut Env) { **env.x += 5; }
+    /// ```
+    /// This is then illegal because you cannot mutate an `&mut` found
+    /// in an aliasable location. To solve, you'd have to translate with
+    /// an `&mut` borrow:
+    /// ```compile_fail,E0596
+    /// struct Env<'a> { x: &'a mut &'a mut isize }
+    /// let mut z = 3;
+    /// let x: &mut isize = &mut z;
+    /// let y = (&mut Env { x: &mut x }, fn_ptr); // changed from &x to &mut x
+    /// fn fn_ptr(env: &mut Env) { **env.x += 5; }
+    /// ```
+    /// Now the assignment to `**env.x` is legal, but creating a
+    /// mutable pointer to `x` is not because `x` is not mutable. We
+    /// could fix this by declaring `x` as `let mut x`. This is ok in
+    /// user code, if awkward, but extra weird for closures, since the
+    /// borrow is hidden.
+    ///
+    /// So we introduce a "unique imm" borrow -- the referent is
+    /// immutable, but not aliasable. This solves the problem. For
+    /// simplicity, we don't give users the way to express this
+    /// borrow, it's just used when translating closures.
+    Unique,
+
+    /// Data is mutable and not aliasable.
+    Mut {
+        /// `true` if this borrow arose from method-call auto-ref
+        /// (i.e., `adjustment::Adjust::Borrow`).
+        allow_two_phase_borrow: bool,
+    },
+}
+
+impl BorrowKind {
+    fn from_hir(m: hir_def::type_ref::Mutability) -> Self {
+        match m {
+            hir_def::type_ref::Mutability::Shared => BorrowKind::Shared,
+            hir_def::type_ref::Mutability::Mut => BorrowKind::Mut { allow_two_phase_borrow: false },
+        }
+    }
+
+    fn from_chalk(m: Mutability) -> Self {
+        match m {
+            Mutability::Not => BorrowKind::Shared,
+            Mutability::Mut => BorrowKind::Mut { allow_two_phase_borrow: false },
+        }
+    }
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub enum UnOp {
+    /// The `!` operator for logical inversion
+    Not,
+    /// The `-` operator for negation
+    Neg,
+}
+
+#[derive(Debug, PartialEq, Eq, Clone)]
+pub enum BinOp {
+    /// The `+` operator (addition)
+    Add,
+    /// The `-` operator (subtraction)
+    Sub,
+    /// The `*` operator (multiplication)
+    Mul,
+    /// The `/` operator (division)
+    ///
+    /// Division by zero is UB, because the compiler should have inserted checks
+    /// prior to this.
+    Div,
+    /// The `%` operator (modulus)
+    ///
+    /// Using zero as the modulus (second operand) is UB, because the compiler
+    /// should have inserted checks prior to this.
+    Rem,
+    /// The `^` operator (bitwise xor)
+    BitXor,
+    /// The `&` operator (bitwise and)
+    BitAnd,
+    /// The `|` operator (bitwise or)
+    BitOr,
+    /// The `<<` operator (shift left)
+    ///
+    /// The offset is truncated to the size of the first operand before shifting.
+    Shl,
+    /// The `>>` operator (shift right)
+    ///
+    /// The offset is truncated to the size of the first operand before shifting.
+    Shr,
+    /// The `==` operator (equality)
+    Eq,
+    /// The `<` operator (less than)
+    Lt,
+    /// The `<=` operator (less than or equal to)
+    Le,
+    /// The `!=` operator (not equal to)
+    Ne,
+    /// The `>=` operator (greater than or equal to)
+    Ge,
+    /// The `>` operator (greater than)
+    Gt,
+    /// The `ptr.offset` operator
+    Offset,
+}
+
+impl Display for BinOp {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        f.write_str(match self {
+            BinOp::Add => "+",
+            BinOp::Sub => "-",
+            BinOp::Mul => "*",
+            BinOp::Div => "/",
+            BinOp::Rem => "%",
+            BinOp::BitXor => "^",
+            BinOp::BitAnd => "&",
+            BinOp::BitOr => "|",
+            BinOp::Shl => "<<",
+            BinOp::Shr => ">>",
+            BinOp::Eq => "==",
+            BinOp::Lt => "<",
+            BinOp::Le => "<=",
+            BinOp::Ne => "!=",
+            BinOp::Ge => ">=",
+            BinOp::Gt => ">",
+            BinOp::Offset => "`offset`",
+        })
+    }
+}
+
+impl From<hir_def::expr::ArithOp> for BinOp {
+    fn from(value: hir_def::expr::ArithOp) -> Self {
+        match value {
+            hir_def::expr::ArithOp::Add => BinOp::Add,
+            hir_def::expr::ArithOp::Mul => BinOp::Mul,
+            hir_def::expr::ArithOp::Sub => BinOp::Sub,
+            hir_def::expr::ArithOp::Div => BinOp::Div,
+            hir_def::expr::ArithOp::Rem => BinOp::Rem,
+            hir_def::expr::ArithOp::Shl => BinOp::Shl,
+            hir_def::expr::ArithOp::Shr => BinOp::Shr,
+            hir_def::expr::ArithOp::BitXor => BinOp::BitXor,
+            hir_def::expr::ArithOp::BitOr => BinOp::BitOr,
+            hir_def::expr::ArithOp::BitAnd => BinOp::BitAnd,
+        }
+    }
+}
+
+impl From<hir_def::expr::CmpOp> for BinOp {
+    fn from(value: hir_def::expr::CmpOp) -> Self {
+        match value {
+            hir_def::expr::CmpOp::Eq { negated: false } => BinOp::Eq,
+            hir_def::expr::CmpOp::Eq { negated: true } => BinOp::Ne,
+            hir_def::expr::CmpOp::Ord { ordering: Ordering::Greater, strict: false } => BinOp::Ge,
+            hir_def::expr::CmpOp::Ord { ordering: Ordering::Greater, strict: true } => BinOp::Gt,
+            hir_def::expr::CmpOp::Ord { ordering: Ordering::Less, strict: false } => BinOp::Le,
+            hir_def::expr::CmpOp::Ord { ordering: Ordering::Less, strict: true } => BinOp::Lt,
+        }
+    }
+}
+
+impl From<Operand> for Rvalue {
+    fn from(x: Operand) -> Self {
+        Self::Use(x)
+    }
+}
+
+#[derive(Debug, PartialEq, Eq, Clone)]
+pub enum CastKind {
+    /// An exposing pointer to address cast. A cast between a pointer and an integer type, or
+    /// between a function pointer and an integer type.
+    /// See the docs on `expose_addr` for more details.
+    PointerExposeAddress,
+    /// An address-to-pointer cast that picks up an exposed provenance.
+    /// See the docs on `from_exposed_addr` for more details.
+    PointerFromExposedAddress,
+    /// All sorts of pointer-to-pointer casts. Note that reference-to-raw-ptr casts are
+    /// translated into `&raw mut/const *r`, i.e., they are not actually casts.
+    Pointer(PointerCast),
+    /// Cast into a dyn* object.
+    DynStar,
+    IntToInt,
+    FloatToInt,
+    FloatToFloat,
+    IntToFloat,
+    PtrToPtr,
+    FnPtrToPtr,
+}
+
+#[derive(Debug, PartialEq, Eq, Clone)]
+pub enum Rvalue {
+    /// Yields the operand unchanged
+    Use(Operand),
+
+    /// Creates an array where each element is the value of the operand.
+    ///
+    /// This is the cause of a bug in the case where the repetition count is zero because the value
+    /// is not dropped, see [#74836].
+    ///
+    /// Corresponds to source code like `[x; 32]`.
+    ///
+    /// [#74836]: https://github.com/rust-lang/rust/issues/74836
+    //Repeat(Operand, ty::Const),
+
+    /// Creates a reference of the indicated kind to the place.
+    ///
+    /// There is not much to document here, because besides the obvious parts the semantics of this
+    /// are essentially entirely a part of the aliasing model. There are many UCG issues discussing
+    /// exactly what the behavior of this operation should be.
+    ///
+    /// `Shallow` borrows are disallowed after drop lowering.
+    Ref(BorrowKind, Place),
+
+    /// Creates a pointer/reference to the given thread local.
+    ///
+    /// The yielded type is a `*mut T` if the static is mutable, otherwise if the static is extern a
+    /// `*const T`, and if neither of those apply a `&T`.
+    ///
+    /// **Note:** This is a runtime operation that actually executes code and is in this sense more
+    /// like a function call. Also, eliminating dead stores of this rvalue causes `fn main() {}` to
+    /// SIGILL for some reason that I (JakobDegen) never got a chance to look into.
+    ///
+    /// **Needs clarification**: Are there weird additional semantics here related to the runtime
+    /// nature of this operation?
+    //ThreadLocalRef(DefId),
+
+    /// Creates a pointer with the indicated mutability to the place.
+    ///
+    /// This is generated by pointer casts like `&v as *const _` or raw address of expressions like
+    /// `&raw v` or `addr_of!(v)`.
+    ///
+    /// Like with references, the semantics of this operation are heavily dependent on the aliasing
+    /// model.
+    //AddressOf(Mutability, Place),
+
+    /// Yields the length of the place, as a `usize`.
+    ///
+    /// If the type of the place is an array, this is the array length. For slices (`[T]`, not
+    /// `&[T]`) this accesses the place's metadata to determine the length. This rvalue is
+    /// ill-formed for places of other types.
+    Len(Place),
+
+    /// Performs essentially all of the casts that can be performed via `as`.
+    ///
+    /// This allows for casts from/to a variety of types.
+    ///
+    /// **FIXME**: Document exactly which `CastKind`s allow which types of casts. Figure out why
+    /// `ArrayToPointer` and `MutToConstPointer` are special.
+    Cast(CastKind, Operand, Ty),
+
+    // FIXME link to `pointer::offset` when it hits stable.
+    /// * `Offset` has the same semantics as `pointer::offset`, except that the second
+    ///   parameter may be a `usize` as well.
+    /// * The comparison operations accept `bool`s, `char`s, signed or unsigned integers, floats,
+    ///   raw pointers, or function pointers and return a `bool`. The types of the operands must be
+    ///   matching, up to the usual caveat of the lifetimes in function pointers.
+    /// * Left and right shift operations accept signed or unsigned integers not necessarily of the
+    ///   same type and return a value of the same type as their LHS. Like in Rust, the RHS is
+    ///   truncated as needed.
+    /// * The `Bit*` operations accept signed integers, unsigned integers, or bools with matching
+    ///   types and return a value of that type.
+    /// * The remaining operations accept signed integers, unsigned integers, or floats with
+    ///   matching types and return a value of that type.
+    //BinaryOp(BinOp, Box<(Operand, Operand)>),
+
+    /// Same as `BinaryOp`, but yields `(T, bool)` with a `bool` indicating an error condition.
+    ///
+    /// When overflow checking is disabled and we are generating run-time code, the error condition
+    /// is false. Otherwise, and always during CTFE, the error condition is determined as described
+    /// below.
+    ///
+    /// For addition, subtraction, and multiplication on integers the error condition is set when
+    /// the infinite precision result would be unequal to the actual result.
+    ///
+    /// For shift operations on integers the error condition is set when the value of right-hand
+    /// side is greater than or equal to the number of bits in the type of the left-hand side, or
+    /// when the value of right-hand side is negative.
+    ///
+    /// Other combinations of types and operators are unsupported.
+    CheckedBinaryOp(BinOp, Operand, Operand),
+
+    /// Computes a value as described by the operation.
+    //NullaryOp(NullOp, Ty),
+
+    /// Exactly like `BinaryOp`, but less operands.
+    ///
+    /// Also does two's-complement arithmetic. Negation requires a signed integer or a float;
+    /// bitwise not requires a signed integer, unsigned integer, or bool. Both operation kinds
+    /// return a value with the same type as their operand.
+    UnaryOp(UnOp, Operand),
+
+    /// Computes the discriminant of the place, returning it as an integer of type
+    /// [`discriminant_ty`]. Returns zero for types without discriminant.
+    ///
+    /// The validity requirements for the underlying value are undecided for this rvalue, see
+    /// [#91095]. Note too that the value of the discriminant is not the same thing as the
+    /// variant index; use [`discriminant_for_variant`] to convert.
+    ///
+    /// [`discriminant_ty`]: crate::ty::Ty::discriminant_ty
+    /// [#91095]: https://github.com/rust-lang/rust/issues/91095
+    /// [`discriminant_for_variant`]: crate::ty::Ty::discriminant_for_variant
+    Discriminant(Place),
+
+    /// Creates an aggregate value, like a tuple or struct.
+    ///
+    /// This is needed because dataflow analysis needs to distinguish
+    /// `dest = Foo { x: ..., y: ... }` from `dest.x = ...; dest.y = ...;` in the case that `Foo`
+    /// has a destructor.
+    ///
+    /// Disallowed after deaggregation for all aggregate kinds except `Array` and `Generator`. After
+    /// generator lowering, `Generator` aggregate kinds are disallowed too.
+    Aggregate(AggregateKind, Vec<Operand>),
+
+    /// Transmutes a `*mut u8` into shallow-initialized `Box<T>`.
+    ///
+    /// This is different from a normal transmute because dataflow analysis will treat the box as
+    /// initialized but its content as uninitialized. Like other pointer casts, this in general
+    /// affects alias analysis.
+    ShallowInitBox(Operand, Ty),
+
+    /// A CopyForDeref is equivalent to a read from a place at the
+    /// codegen level, but is treated specially by drop elaboration. When such a read happens, it
+    /// is guaranteed (via nature of the mir_opt `Derefer` in rustc_mir_transform/src/deref_separator)
+    /// that the only use of the returned value is a deref operation, immediately
+    /// followed by one or more projections. Drop elaboration treats this rvalue as if the
+    /// read never happened and just projects further. This allows simplifying various MIR
+    /// optimizations and codegen backends that previously had to handle deref operations anywhere
+    /// in a place.
+    CopyForDeref(Place),
+}
+
+#[derive(Debug, PartialEq, Eq, Clone)]
+pub enum StatementKind {
+    Assign(Place, Rvalue),
+    //FakeRead(Box<(FakeReadCause, Place)>),
+    //SetDiscriminant {
+    //    place: Box<Place>,
+    //    variant_index: VariantIdx,
+    //},
+    Deinit(Place),
+    StorageLive(LocalId),
+    StorageDead(LocalId),
+    //Retag(RetagKind, Box<Place>),
+    //AscribeUserType(Place, UserTypeProjection, Variance),
+    //Intrinsic(Box<NonDivergingIntrinsic>),
+    Nop,
+}
+impl StatementKind {
+    fn with_span(self, span: MirSpan) -> Statement {
+        Statement { kind: self, span }
+    }
+}
+
+#[derive(Debug, PartialEq, Eq, Clone)]
+pub struct Statement {
+    pub kind: StatementKind,
+    pub span: MirSpan,
+}
+
+#[derive(Debug, Default, PartialEq, Eq)]
+pub struct BasicBlock {
+    /// List of statements in this block.
+    pub statements: Vec<Statement>,
+
+    /// Terminator for this block.
+    ///
+    /// N.B., this should generally ONLY be `None` during construction.
+    /// Therefore, you should generally access it via the
+    /// `terminator()` or `terminator_mut()` methods. The only
+    /// exception is that certain passes, such as `simplify_cfg`, swap
+    /// out the terminator temporarily with `None` while they continue
+    /// to recurse over the set of basic blocks.
+    pub terminator: Option<Terminator>,
+
+    /// If true, this block lies on an unwind path. This is used
+    /// during codegen where distinct kinds of basic blocks may be
+    /// generated (particularly for MSVC cleanup). Unwind blocks must
+    /// only branch to other unwind blocks.
+    pub is_cleanup: bool,
+}
+
+#[derive(Debug, PartialEq, Eq)]
+pub struct MirBody {
+    pub basic_blocks: Arena<BasicBlock>,
+    pub locals: Arena<Local>,
+    pub start_block: BasicBlockId,
+    pub owner: DefWithBodyId,
+    pub arg_count: usize,
+    pub binding_locals: ArenaMap<BindingId, LocalId>,
+    pub param_locals: Vec<LocalId>,
+}
+
+fn const_as_usize(c: &Const) -> usize {
+    try_const_usize(c).unwrap() as usize
+}
+
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+pub enum MirSpan {
+    ExprId(ExprId),
+    PatId(PatId),
+    Unknown,
+}
+
+impl_from!(ExprId, PatId for MirSpan);
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs
new file mode 100644
index 00000000000..c8729af86a9
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs
@@ -0,0 +1,223 @@
+//! MIR borrow checker, which is used in diagnostics like `unused_mut`
+
+// Currently it is an ad-hoc implementation, only useful for mutability analysis. Feel free to remove all of these
+// if needed for implementing a proper borrow checker.
+
+use std::sync::Arc;
+
+use hir_def::DefWithBodyId;
+use la_arena::ArenaMap;
+use stdx::never;
+
+use crate::db::HirDatabase;
+
+use super::{
+    BasicBlockId, BorrowKind, LocalId, MirBody, MirLowerError, MirSpan, Place, ProjectionElem,
+    Rvalue, StatementKind, Terminator,
+};
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+/// Stores spans which implies that the local should be mutable.
+pub enum MutabilityReason {
+    Mut { spans: Vec<MirSpan> },
+    Not,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct BorrowckResult {
+    pub mir_body: Arc<MirBody>,
+    pub mutability_of_locals: ArenaMap<LocalId, MutabilityReason>,
+}
+
+pub fn borrowck_query(
+    db: &dyn HirDatabase,
+    def: DefWithBodyId,
+) -> Result<Arc<BorrowckResult>, MirLowerError> {
+    let _p = profile::span("borrowck_query");
+    let body = db.mir_body(def)?;
+    let r = BorrowckResult { mutability_of_locals: mutability_of_locals(&body), mir_body: body };
+    Ok(Arc::new(r))
+}
+
+fn is_place_direct(lvalue: &Place) -> bool {
+    !lvalue.projection.iter().any(|x| *x == ProjectionElem::Deref)
+}
+
+enum ProjectionCase {
+    /// Projection is a local
+    Direct,
+    /// Projection is some field or slice of a local
+    DirectPart,
+    /// Projection is deref of something
+    Indirect,
+}
+
+fn place_case(lvalue: &Place) -> ProjectionCase {
+    let mut is_part_of = false;
+    for proj in lvalue.projection.iter().rev() {
+        match proj {
+            ProjectionElem::Deref => return ProjectionCase::Indirect, // It's indirect
+            ProjectionElem::ConstantIndex { .. }
+            | ProjectionElem::Subslice { .. }
+            | ProjectionElem::Field(_)
+            | ProjectionElem::TupleField(_)
+            | ProjectionElem::Index(_) => {
+                is_part_of = true;
+            }
+            ProjectionElem::OpaqueCast(_) => (),
+        }
+    }
+    if is_part_of {
+        ProjectionCase::DirectPart
+    } else {
+        ProjectionCase::Direct
+    }
+}
+
+/// Returns a map from basic blocks to the set of locals that might be ever initialized before
+/// the start of the block. Only `StorageDead` can remove something from this map, and we ignore
+/// `Uninit` and `drop` and similars after initialization.
+fn ever_initialized_map(body: &MirBody) -> ArenaMap<BasicBlockId, ArenaMap<LocalId, bool>> {
+    let mut result: ArenaMap<BasicBlockId, ArenaMap<LocalId, bool>> =
+        body.basic_blocks.iter().map(|x| (x.0, ArenaMap::default())).collect();
+    fn dfs(
+        body: &MirBody,
+        b: BasicBlockId,
+        l: LocalId,
+        result: &mut ArenaMap<BasicBlockId, ArenaMap<LocalId, bool>>,
+    ) {
+        let mut is_ever_initialized = result[b][l]; // It must be filled, as we use it as mark for dfs
+        let block = &body.basic_blocks[b];
+        for statement in &block.statements {
+            match &statement.kind {
+                StatementKind::Assign(p, _) => {
+                    if p.projection.len() == 0 && p.local == l {
+                        is_ever_initialized = true;
+                    }
+                }
+                StatementKind::StorageDead(p) => {
+                    if *p == l {
+                        is_ever_initialized = false;
+                    }
+                }
+                StatementKind::Deinit(_) | StatementKind::Nop | StatementKind::StorageLive(_) => (),
+            }
+        }
+        let Some(terminator) = &block.terminator else {
+            never!("Terminator should be none only in construction");
+            return;
+        };
+        let targets = match terminator {
+            Terminator::Goto { target } => vec![*target],
+            Terminator::SwitchInt { targets, .. } => targets.all_targets().to_vec(),
+            Terminator::Resume
+            | Terminator::Abort
+            | Terminator::Return
+            | Terminator::Unreachable => vec![],
+            Terminator::Call { target, cleanup, destination, .. } => {
+                if destination.projection.len() == 0 && destination.local == l {
+                    is_ever_initialized = true;
+                }
+                target.into_iter().chain(cleanup.into_iter()).copied().collect()
+            }
+            Terminator::Drop { .. }
+            | Terminator::DropAndReplace { .. }
+            | Terminator::Assert { .. }
+            | Terminator::Yield { .. }
+            | Terminator::GeneratorDrop
+            | Terminator::FalseEdge { .. }
+            | Terminator::FalseUnwind { .. } => {
+                never!("We don't emit these MIR terminators yet");
+                vec![]
+            }
+        };
+        for target in targets {
+            if !result[target].contains_idx(l) || !result[target][l] && is_ever_initialized {
+                result[target].insert(l, is_ever_initialized);
+                dfs(body, target, l, result);
+            }
+        }
+    }
+    for &l in &body.param_locals {
+        result[body.start_block].insert(l, true);
+        dfs(body, body.start_block, l, &mut result);
+    }
+    for l in body.locals.iter().map(|x| x.0) {
+        if !result[body.start_block].contains_idx(l) {
+            result[body.start_block].insert(l, false);
+            dfs(body, body.start_block, l, &mut result);
+        }
+    }
+    result
+}
+
+fn mutability_of_locals(body: &MirBody) -> ArenaMap<LocalId, MutabilityReason> {
+    let mut result: ArenaMap<LocalId, MutabilityReason> =
+        body.locals.iter().map(|x| (x.0, MutabilityReason::Not)).collect();
+    let mut push_mut_span = |local, span| match &mut result[local] {
+        MutabilityReason::Mut { spans } => spans.push(span),
+        x @ MutabilityReason::Not => *x = MutabilityReason::Mut { spans: vec![span] },
+    };
+    let ever_init_maps = ever_initialized_map(body);
+    for (block_id, mut ever_init_map) in ever_init_maps.into_iter() {
+        let block = &body.basic_blocks[block_id];
+        for statement in &block.statements {
+            match &statement.kind {
+                StatementKind::Assign(place, value) => {
+                    match place_case(place) {
+                        ProjectionCase::Direct => {
+                            if ever_init_map.get(place.local).copied().unwrap_or_default() {
+                                push_mut_span(place.local, statement.span);
+                            } else {
+                                ever_init_map.insert(place.local, true);
+                            }
+                        }
+                        ProjectionCase::DirectPart => {
+                            // Partial initialization is not supported, so it is definitely `mut`
+                            push_mut_span(place.local, statement.span);
+                        }
+                        ProjectionCase::Indirect => (),
+                    }
+                    if let Rvalue::Ref(BorrowKind::Mut { .. }, p) = value {
+                        if is_place_direct(p) {
+                            push_mut_span(p.local, statement.span);
+                        }
+                    }
+                }
+                StatementKind::StorageDead(p) => {
+                    ever_init_map.insert(*p, false);
+                }
+                StatementKind::Deinit(_) | StatementKind::StorageLive(_) | StatementKind::Nop => (),
+            }
+        }
+        let Some(terminator) = &block.terminator else {
+            never!("Terminator should be none only in construction");
+            continue;
+        };
+        match terminator {
+            Terminator::Goto { .. }
+            | Terminator::Resume
+            | Terminator::Abort
+            | Terminator::Return
+            | Terminator::Unreachable
+            | Terminator::FalseEdge { .. }
+            | Terminator::FalseUnwind { .. }
+            | Terminator::GeneratorDrop
+            | Terminator::SwitchInt { .. }
+            | Terminator::Drop { .. }
+            | Terminator::DropAndReplace { .. }
+            | Terminator::Assert { .. }
+            | Terminator::Yield { .. } => (),
+            Terminator::Call { destination, .. } => {
+                if destination.projection.len() == 0 {
+                    if ever_init_map.get(destination.local).copied().unwrap_or_default() {
+                        push_mut_span(destination.local, MirSpan::Unknown);
+                    } else {
+                        ever_init_map.insert(destination.local, true);
+                    }
+                }
+            }
+        }
+    }
+    result
+}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs
new file mode 100644
index 00000000000..c5d843d9ebd
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs
@@ -0,0 +1,1253 @@
+//! This module provides a MIR interpreter, which is used in const eval.
+
+use std::{borrow::Cow, collections::HashMap, iter};
+
+use base_db::CrateId;
+use chalk_ir::{
+    fold::{FallibleTypeFolder, TypeFoldable, TypeSuperFoldable},
+    DebruijnIndex, TyKind,
+};
+use hir_def::{
+    builtin_type::BuiltinType,
+    lang_item::{lang_attr, LangItem},
+    layout::{Layout, LayoutError, RustcEnumVariantIdx, TagEncoding, Variants},
+    AdtId, DefWithBodyId, EnumVariantId, FunctionId, HasModule, Lookup, VariantId,
+};
+use intern::Interned;
+use la_arena::ArenaMap;
+
+use crate::{
+    consteval::{intern_const_scalar, ConstEvalError},
+    db::HirDatabase,
+    from_placeholder_idx,
+    infer::{normalize, PointerCast},
+    layout::layout_of_ty,
+    mapping::from_chalk,
+    method_resolution::lookup_impl_method,
+    CallableDefId, Const, ConstScalar, Interner, MemoryMap, Substitution, Ty, TyBuilder, TyExt,
+};
+
+use super::{
+    const_as_usize, return_slot, AggregateKind, BinOp, CastKind, LocalId, MirBody, MirLowerError,
+    Operand, Place, ProjectionElem, Rvalue, StatementKind, Terminator, UnOp,
+};
+
+pub struct Evaluator<'a> {
+    db: &'a dyn HirDatabase,
+    stack: Vec<u8>,
+    heap: Vec<u8>,
+    crate_id: CrateId,
+    // FIXME: This is a workaround, see the comment on `interpret_mir`
+    assert_placeholder_ty_is_unused: bool,
+    /// A general limit on execution, to prevent non terminating programs from breaking r-a main process
+    execution_limit: usize,
+    /// An additional limit on stack depth, to prevent stack overflow
+    stack_depth_limit: usize,
+}
+
+#[derive(Debug, Clone, Copy)]
+enum Address {
+    Stack(usize),
+    Heap(usize),
+}
+
+use Address::*;
+
+struct Interval {
+    addr: Address,
+    size: usize,
+}
+
+impl Interval {
+    fn new(addr: Address, size: usize) -> Self {
+        Self { addr, size }
+    }
+
+    fn get<'a>(&self, memory: &'a Evaluator<'a>) -> Result<&'a [u8]> {
+        memory.read_memory(self.addr, self.size)
+    }
+}
+
+enum IntervalOrOwned {
+    Owned(Vec<u8>),
+    Borrowed(Interval),
+}
+impl IntervalOrOwned {
+    pub(crate) fn to_vec(self, memory: &Evaluator<'_>) -> Result<Vec<u8>> {
+        Ok(match self {
+            IntervalOrOwned::Owned(o) => o,
+            IntervalOrOwned::Borrowed(b) => b.get(memory)?.to_vec(),
+        })
+    }
+}
+
+macro_rules! from_bytes {
+    ($ty:tt, $value:expr) => {
+        ($ty::from_le_bytes(match ($value).try_into() {
+            Ok(x) => x,
+            Err(_) => return Err(MirEvalError::TypeError("mismatched size")),
+        }))
+    };
+}
+
+impl Address {
+    fn from_bytes(x: &[u8]) -> Result<Self> {
+        Ok(Address::from_usize(from_bytes!(usize, x)))
+    }
+
+    fn from_usize(x: usize) -> Self {
+        if x > usize::MAX / 2 {
+            Stack(usize::MAX - x)
+        } else {
+            Heap(x)
+        }
+    }
+
+    fn to_bytes(&self) -> Vec<u8> {
+        usize::to_le_bytes(self.to_usize()).to_vec()
+    }
+
+    fn to_usize(&self) -> usize {
+        let as_num = match self {
+            Stack(x) => usize::MAX - *x,
+            Heap(x) => *x,
+        };
+        as_num
+    }
+
+    fn map(&self, f: impl FnOnce(usize) -> usize) -> Address {
+        match self {
+            Stack(x) => Stack(f(*x)),
+            Heap(x) => Heap(f(*x)),
+        }
+    }
+
+    fn offset(&self, offset: usize) -> Address {
+        self.map(|x| x + offset)
+    }
+}
+
+#[derive(Clone, PartialEq, Eq)]
+pub enum MirEvalError {
+    ConstEvalError(Box<ConstEvalError>),
+    LayoutError(LayoutError, Ty),
+    /// Means that code had type errors (or mismatched args) and we shouldn't generate mir in first place.
+    TypeError(&'static str),
+    /// Means that code had undefined behavior. We don't try to actively detect UB, but if it was detected
+    /// then use this type of error.
+    UndefinedBehavior(&'static str),
+    Panic,
+    MirLowerError(FunctionId, MirLowerError),
+    TypeIsUnsized(Ty, &'static str),
+    NotSupported(String),
+    InvalidConst(Const),
+    InFunction(FunctionId, Box<MirEvalError>),
+    ExecutionLimitExceeded,
+    StackOverflow,
+    TargetDataLayoutNotAvailable,
+}
+
+impl std::fmt::Debug for MirEvalError {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            Self::ConstEvalError(arg0) => f.debug_tuple("ConstEvalError").field(arg0).finish(),
+            Self::LayoutError(arg0, arg1) => {
+                f.debug_tuple("LayoutError").field(arg0).field(arg1).finish()
+            }
+            Self::TypeError(arg0) => f.debug_tuple("TypeError").field(arg0).finish(),
+            Self::UndefinedBehavior(arg0) => {
+                f.debug_tuple("UndefinedBehavior").field(arg0).finish()
+            }
+            Self::Panic => write!(f, "Panic"),
+            Self::TargetDataLayoutNotAvailable => write!(f, "TargetDataLayoutNotAvailable"),
+            Self::TypeIsUnsized(ty, it) => write!(f, "{ty:?} is unsized. {it} should be sized."),
+            Self::ExecutionLimitExceeded => write!(f, "execution limit exceeded"),
+            Self::StackOverflow => write!(f, "stack overflow"),
+            Self::MirLowerError(arg0, arg1) => {
+                f.debug_tuple("MirLowerError").field(arg0).field(arg1).finish()
+            }
+            Self::NotSupported(arg0) => f.debug_tuple("NotSupported").field(arg0).finish(),
+            Self::InvalidConst(arg0) => {
+                let data = &arg0.data(Interner);
+                f.debug_struct("InvalidConst").field("ty", &data.ty).field("value", &arg0).finish()
+            }
+            Self::InFunction(func, e) => {
+                let mut e = &**e;
+                let mut stack = vec![*func];
+                while let Self::InFunction(f, next_e) = e {
+                    e = &next_e;
+                    stack.push(*f);
+                }
+                f.debug_struct("WithStack").field("error", e).field("stack", &stack).finish()
+            }
+        }
+    }
+}
+
+macro_rules! not_supported {
+    ($x: expr) => {
+        return Err(MirEvalError::NotSupported(format!($x)))
+    };
+}
+
+impl From<ConstEvalError> for MirEvalError {
+    fn from(value: ConstEvalError) -> Self {
+        match value {
+            _ => MirEvalError::ConstEvalError(Box::new(value)),
+        }
+    }
+}
+
+type Result<T> = std::result::Result<T, MirEvalError>;
+
+struct Locals<'a> {
+    ptr: &'a ArenaMap<LocalId, Address>,
+    body: &'a MirBody,
+    subst: &'a Substitution,
+}
+
+pub fn interpret_mir(
+    db: &dyn HirDatabase,
+    body: &MirBody,
+    // FIXME: This is workaround. Ideally, const generics should have a separate body (issue #7434), but now
+    // they share their body with their parent, so in MIR lowering we have locals of the parent body, which
+    // might have placeholders. With this argument, we (wrongly) assume that every placeholder type has
+    // a zero size, hoping that they are all outside of our current body. Even without a fix for #7434, we can
+    // (and probably should) do better here, for example by excluding bindings outside of the target expression.
+    assert_placeholder_ty_is_unused: bool,
+) -> Result<Const> {
+    let ty = body.locals[return_slot()].ty.clone();
+    let mut evaluator =
+        Evaluator::new(db, body.owner.module(db.upcast()).krate(), assert_placeholder_ty_is_unused);
+    let bytes = evaluator.interpret_mir_with_no_arg(&body)?;
+    let memory_map = evaluator.create_memory_map(
+        &bytes,
+        &ty,
+        &Locals { ptr: &ArenaMap::new(), body: &body, subst: &Substitution::empty(Interner) },
+    )?;
+    return Ok(intern_const_scalar(ConstScalar::Bytes(bytes, memory_map), ty));
+}
+
+impl Evaluator<'_> {
+    pub fn new<'a>(
+        db: &'a dyn HirDatabase,
+        crate_id: CrateId,
+        assert_placeholder_ty_is_unused: bool,
+    ) -> Evaluator<'a> {
+        Evaluator {
+            stack: vec![0],
+            heap: vec![0],
+            db,
+            crate_id,
+            assert_placeholder_ty_is_unused,
+            stack_depth_limit: 100,
+            execution_limit: 100_000,
+        }
+    }
+
+    fn place_addr(&self, p: &Place, locals: &Locals<'_>) -> Result<Address> {
+        Ok(self.place_addr_and_ty(p, locals)?.0)
+    }
+
+    fn ptr_size(&self) -> usize {
+        match self.db.target_data_layout(self.crate_id) {
+            Some(x) => x.pointer_size.bytes_usize(),
+            None => 8,
+        }
+    }
+
+    fn place_addr_and_ty<'a>(&'a self, p: &Place, locals: &'a Locals<'a>) -> Result<(Address, Ty)> {
+        let mut addr = locals.ptr[p.local];
+        let mut ty: Ty =
+            self.ty_filler(&locals.body.locals[p.local].ty, locals.subst, locals.body.owner)?;
+        for proj in &p.projection {
+            match proj {
+                ProjectionElem::Deref => {
+                    ty = match &ty.data(Interner).kind {
+                        TyKind::Raw(_, inner) | TyKind::Ref(_, _, inner) => inner.clone(),
+                        _ => {
+                            return Err(MirEvalError::TypeError(
+                                "Overloaded deref in MIR is disallowed",
+                            ))
+                        }
+                    };
+                    let x = from_bytes!(usize, self.read_memory(addr, self.ptr_size())?);
+                    addr = Address::from_usize(x);
+                }
+                ProjectionElem::Index(op) => {
+                    let offset =
+                        from_bytes!(usize, self.read_memory(locals.ptr[*op], self.ptr_size())?);
+                    match &ty.data(Interner).kind {
+                        TyKind::Ref(_, _, inner) => match &inner.data(Interner).kind {
+                            TyKind::Slice(inner) => {
+                                ty = inner.clone();
+                                let ty_size = self.size_of_sized(
+                                    &ty,
+                                    locals,
+                                    "slice inner type should be sized",
+                                )?;
+                                let value = self.read_memory(addr, self.ptr_size() * 2)?;
+                                addr = Address::from_bytes(&value[0..8])?.offset(ty_size * offset);
+                            }
+                            x => not_supported!("MIR index for ref type {x:?}"),
+                        },
+                        TyKind::Array(inner, _) | TyKind::Slice(inner) => {
+                            ty = inner.clone();
+                            let ty_size = self.size_of_sized(
+                                &ty,
+                                locals,
+                                "array inner type should be sized",
+                            )?;
+                            addr = addr.offset(ty_size * offset);
+                        }
+                        x => not_supported!("MIR index for type {x:?}"),
+                    }
+                }
+                &ProjectionElem::TupleField(f) => match &ty.data(Interner).kind {
+                    TyKind::Tuple(_, subst) => {
+                        let layout = self.layout(&ty)?;
+                        ty = subst
+                            .as_slice(Interner)
+                            .get(f)
+                            .ok_or(MirEvalError::TypeError("not enough tuple fields"))?
+                            .assert_ty_ref(Interner)
+                            .clone();
+                        let offset = layout.fields.offset(f).bytes_usize();
+                        addr = addr.offset(offset);
+                    }
+                    _ => return Err(MirEvalError::TypeError("Only tuple has tuple fields")),
+                },
+                ProjectionElem::Field(f) => match &ty.data(Interner).kind {
+                    TyKind::Adt(adt, subst) => {
+                        let layout = self.layout_adt(adt.0, subst.clone())?;
+                        let variant_layout = match &layout.variants {
+                            Variants::Single { .. } => &layout,
+                            Variants::Multiple { variants, .. } => {
+                                &variants[match f.parent {
+                                    hir_def::VariantId::EnumVariantId(x) => {
+                                        RustcEnumVariantIdx(x.local_id)
+                                    }
+                                    _ => {
+                                        return Err(MirEvalError::TypeError(
+                                            "Multivariant layout only happens for enums",
+                                        ))
+                                    }
+                                }]
+                            }
+                        };
+                        ty = self.db.field_types(f.parent)[f.local_id]
+                            .clone()
+                            .substitute(Interner, subst);
+                        let offset = variant_layout
+                            .fields
+                            .offset(u32::from(f.local_id.into_raw()) as usize)
+                            .bytes_usize();
+                        addr = addr.offset(offset);
+                    }
+                    _ => return Err(MirEvalError::TypeError("Only adt has fields")),
+                },
+                ProjectionElem::ConstantIndex { .. } => {
+                    not_supported!("constant index")
+                }
+                ProjectionElem::Subslice { .. } => not_supported!("subslice"),
+                ProjectionElem::OpaqueCast(_) => not_supported!("opaque cast"),
+            }
+        }
+        Ok((addr, ty))
+    }
+
+    fn layout(&self, ty: &Ty) -> Result<Layout> {
+        layout_of_ty(self.db, ty, self.crate_id)
+            .map_err(|e| MirEvalError::LayoutError(e, ty.clone()))
+    }
+
+    fn layout_adt(&self, adt: AdtId, subst: Substitution) -> Result<Layout> {
+        self.db.layout_of_adt(adt, subst.clone()).map_err(|e| {
+            MirEvalError::LayoutError(e, TyKind::Adt(chalk_ir::AdtId(adt), subst).intern(Interner))
+        })
+    }
+
+    fn place_ty<'a>(&'a self, p: &Place, locals: &'a Locals<'a>) -> Result<Ty> {
+        Ok(self.place_addr_and_ty(p, locals)?.1)
+    }
+
+    fn operand_ty<'a>(&'a self, o: &'a Operand, locals: &'a Locals<'a>) -> Result<Ty> {
+        Ok(match o {
+            Operand::Copy(p) | Operand::Move(p) => self.place_ty(p, locals)?,
+            Operand::Constant(c) => c.data(Interner).ty.clone(),
+        })
+    }
+
+    fn interpret_mir(
+        &mut self,
+        body: &MirBody,
+        args: impl Iterator<Item = Vec<u8>>,
+        subst: Substitution,
+    ) -> Result<Vec<u8>> {
+        if let Some(x) = self.stack_depth_limit.checked_sub(1) {
+            self.stack_depth_limit = x;
+        } else {
+            return Err(MirEvalError::StackOverflow);
+        }
+        let mut current_block_idx = body.start_block;
+        let mut locals = Locals { ptr: &ArenaMap::new(), body: &body, subst: &subst };
+        let (locals_ptr, stack_size) = {
+            let mut stack_ptr = self.stack.len();
+            let addr = body
+                .locals
+                .iter()
+                .map(|(id, x)| {
+                    let size =
+                        self.size_of_sized(&x.ty, &locals, "no unsized local in extending stack")?;
+                    let my_ptr = stack_ptr;
+                    stack_ptr += size;
+                    Ok((id, Stack(my_ptr)))
+                })
+                .collect::<Result<ArenaMap<LocalId, _>>>()?;
+            let stack_size = stack_ptr - self.stack.len();
+            (addr, stack_size)
+        };
+        locals.ptr = &locals_ptr;
+        self.stack.extend(iter::repeat(0).take(stack_size));
+        let mut remain_args = body.arg_count;
+        for ((_, addr), value) in locals_ptr.iter().skip(1).zip(args) {
+            self.write_memory(*addr, &value)?;
+            if remain_args == 0 {
+                return Err(MirEvalError::TypeError("more arguments provided"));
+            }
+            remain_args -= 1;
+        }
+        if remain_args > 0 {
+            return Err(MirEvalError::TypeError("not enough arguments provided"));
+        }
+        loop {
+            let current_block = &body.basic_blocks[current_block_idx];
+            if let Some(x) = self.execution_limit.checked_sub(1) {
+                self.execution_limit = x;
+            } else {
+                return Err(MirEvalError::ExecutionLimitExceeded);
+            }
+            for statement in &current_block.statements {
+                match &statement.kind {
+                    StatementKind::Assign(l, r) => {
+                        let addr = self.place_addr(l, &locals)?;
+                        let result = self.eval_rvalue(r, &locals)?.to_vec(&self)?;
+                        self.write_memory(addr, &result)?;
+                    }
+                    StatementKind::Deinit(_) => not_supported!("de-init statement"),
+                    StatementKind::StorageLive(_)
+                    | StatementKind::StorageDead(_)
+                    | StatementKind::Nop => (),
+                }
+            }
+            let Some(terminator) = current_block.terminator.as_ref() else {
+                not_supported!("block without terminator");
+            };
+            match terminator {
+                Terminator::Goto { target } => {
+                    current_block_idx = *target;
+                }
+                Terminator::Call {
+                    func,
+                    args,
+                    destination,
+                    target,
+                    cleanup: _,
+                    from_hir_call: _,
+                } => {
+                    let fn_ty = self.operand_ty(func, &locals)?;
+                    match &fn_ty.data(Interner).kind {
+                        TyKind::FnDef(def, generic_args) => {
+                            let def: CallableDefId = from_chalk(self.db, *def);
+                            let generic_args = self.subst_filler(generic_args, &locals);
+                            match def {
+                                CallableDefId::FunctionId(def) => {
+                                    let arg_bytes = args
+                                        .iter()
+                                        .map(|x| {
+                                            Ok(self
+                                                .eval_operand(x, &locals)?
+                                                .get(&self)?
+                                                .to_owned())
+                                        })
+                                        .collect::<Result<Vec<_>>>()?
+                                        .into_iter();
+                                    let function_data = self.db.function_data(def);
+                                    let is_intrinsic = match &function_data.abi {
+                                        Some(abi) => *abi == Interned::new_str("rust-intrinsic"),
+                                        None => match def.lookup(self.db.upcast()).container {
+                                            hir_def::ItemContainerId::ExternBlockId(block) => {
+                                                let id = block.lookup(self.db.upcast()).id;
+                                                id.item_tree(self.db.upcast())[id.value]
+                                                    .abi
+                                                    .as_deref()
+                                                    == Some("rust-intrinsic")
+                                            }
+                                            _ => false,
+                                        },
+                                    };
+                                    let result = if is_intrinsic {
+                                        self.exec_intrinsic(
+                                            function_data
+                                                .name
+                                                .as_text()
+                                                .unwrap_or_default()
+                                                .as_str(),
+                                            arg_bytes,
+                                            generic_args,
+                                            &locals,
+                                        )?
+                                    } else if let Some(x) = self.detect_lang_function(def) {
+                                        self.exec_lang_item(x, arg_bytes)?
+                                    } else {
+                                        let trait_env = {
+                                            let Some(d) = body.owner.as_generic_def_id() else {
+                                                not_supported!("trait resolving in non generic def id");
+                                            };
+                                            self.db.trait_environment(d)
+                                        };
+                                        let (imp, generic_args) = lookup_impl_method(
+                                            self.db,
+                                            trait_env,
+                                            def,
+                                            generic_args.clone(),
+                                        );
+                                        let generic_args =
+                                            self.subst_filler(&generic_args, &locals);
+                                        let def = imp.into();
+                                        let mir_body = self
+                                            .db
+                                            .mir_body(def)
+                                            .map_err(|e| MirEvalError::MirLowerError(imp, e))?;
+                                        self.interpret_mir(&mir_body, arg_bytes, generic_args)
+                                            .map_err(|e| {
+                                                MirEvalError::InFunction(imp, Box::new(e))
+                                            })?
+                                    };
+                                    let dest_addr = self.place_addr(destination, &locals)?;
+                                    self.write_memory(dest_addr, &result)?;
+                                }
+                                CallableDefId::StructId(id) => {
+                                    let (size, variant_layout, tag) = self.layout_of_variant(
+                                        id.into(),
+                                        generic_args.clone(),
+                                        &locals,
+                                    )?;
+                                    let result = self.make_by_layout(
+                                        size,
+                                        &variant_layout,
+                                        tag,
+                                        args,
+                                        &locals,
+                                    )?;
+                                    let dest_addr = self.place_addr(destination, &locals)?;
+                                    self.write_memory(dest_addr, &result)?;
+                                }
+                                CallableDefId::EnumVariantId(id) => {
+                                    let (size, variant_layout, tag) = self.layout_of_variant(
+                                        id.into(),
+                                        generic_args.clone(),
+                                        &locals,
+                                    )?;
+                                    let result = self.make_by_layout(
+                                        size,
+                                        &variant_layout,
+                                        tag,
+                                        args,
+                                        &locals,
+                                    )?;
+                                    let dest_addr = self.place_addr(destination, &locals)?;
+                                    self.write_memory(dest_addr, &result)?;
+                                }
+                            }
+                            current_block_idx =
+                                target.expect("broken mir, function without target");
+                        }
+                        _ => not_supported!("unknown function type"),
+                    }
+                }
+                Terminator::SwitchInt { discr, targets } => {
+                    let val = u128::from_le_bytes(pad16(
+                        self.eval_operand(discr, &locals)?.get(&self)?,
+                        false,
+                    ));
+                    current_block_idx = targets.target_for_value(val);
+                }
+                Terminator::Return => {
+                    let ty = body.locals[return_slot()].ty.clone();
+                    self.stack_depth_limit += 1;
+                    return Ok(self
+                        .read_memory(
+                            locals.ptr[return_slot()],
+                            self.size_of_sized(&ty, &locals, "return type")?,
+                        )?
+                        .to_owned());
+                }
+                Terminator::Unreachable => {
+                    return Err(MirEvalError::UndefinedBehavior("unreachable executed"))
+                }
+                _ => not_supported!("unknown terminator"),
+            }
+        }
+    }
+
+    fn eval_rvalue<'a>(
+        &'a mut self,
+        r: &'a Rvalue,
+        locals: &'a Locals<'a>,
+    ) -> Result<IntervalOrOwned> {
+        use IntervalOrOwned::*;
+        Ok(match r {
+            Rvalue::Use(x) => Borrowed(self.eval_operand(x, locals)?),
+            Rvalue::Ref(_, p) => {
+                let addr = self.place_addr(p, locals)?;
+                Owned(addr.to_bytes())
+            }
+            Rvalue::Len(_) => not_supported!("rvalue len"),
+            Rvalue::UnaryOp(op, val) => {
+                let mut c = self.eval_operand(val, locals)?.get(&self)?;
+                let mut ty = self.operand_ty(val, locals)?;
+                while let TyKind::Ref(_, _, z) = ty.kind(Interner) {
+                    ty = z.clone();
+                    let size = self.size_of_sized(&ty, locals, "operand of unary op")?;
+                    c = self.read_memory(Address::from_bytes(c)?, size)?;
+                }
+                let mut c = c.to_vec();
+                if ty.as_builtin() == Some(BuiltinType::Bool) {
+                    c[0] = 1 - c[0];
+                } else {
+                    match op {
+                        UnOp::Not => c.iter_mut().for_each(|x| *x = !*x),
+                        UnOp::Neg => {
+                            c.iter_mut().for_each(|x| *x = !*x);
+                            for k in c.iter_mut() {
+                                let o;
+                                (*k, o) = k.overflowing_add(1);
+                                if !o {
+                                    break;
+                                }
+                            }
+                        }
+                    }
+                }
+                Owned(c)
+            }
+            Rvalue::CheckedBinaryOp(op, lhs, rhs) => {
+                let lc = self.eval_operand(lhs, locals)?;
+                let rc = self.eval_operand(rhs, locals)?;
+                let mut lc = lc.get(&self)?;
+                let mut rc = rc.get(&self)?;
+                let mut ty = self.operand_ty(lhs, locals)?;
+                while let TyKind::Ref(_, _, z) = ty.kind(Interner) {
+                    ty = z.clone();
+                    let size = self.size_of_sized(&ty, locals, "operand of binary op")?;
+                    lc = self.read_memory(Address::from_bytes(lc)?, size)?;
+                    rc = self.read_memory(Address::from_bytes(rc)?, size)?;
+                }
+                let is_signed = matches!(ty.as_builtin(), Some(BuiltinType::Int(_)));
+                let l128 = i128::from_le_bytes(pad16(lc, is_signed));
+                let r128 = i128::from_le_bytes(pad16(rc, is_signed));
+                match op {
+                    BinOp::Ge | BinOp::Gt | BinOp::Le | BinOp::Lt | BinOp::Eq | BinOp::Ne => {
+                        let r = match op {
+                            BinOp::Ge => l128 >= r128,
+                            BinOp::Gt => l128 > r128,
+                            BinOp::Le => l128 <= r128,
+                            BinOp::Lt => l128 < r128,
+                            BinOp::Eq => l128 == r128,
+                            BinOp::Ne => l128 != r128,
+                            _ => unreachable!(),
+                        };
+                        let r = r as u8;
+                        Owned(vec![r])
+                    }
+                    BinOp::BitAnd
+                    | BinOp::BitOr
+                    | BinOp::BitXor
+                    | BinOp::Add
+                    | BinOp::Mul
+                    | BinOp::Div
+                    | BinOp::Rem
+                    | BinOp::Sub => {
+                        let r = match op {
+                            BinOp::Add => l128.overflowing_add(r128).0,
+                            BinOp::Mul => l128.overflowing_mul(r128).0,
+                            BinOp::Div => l128.checked_div(r128).ok_or(MirEvalError::Panic)?,
+                            BinOp::Rem => l128.checked_rem(r128).ok_or(MirEvalError::Panic)?,
+                            BinOp::Sub => l128.overflowing_sub(r128).0,
+                            BinOp::BitAnd => l128 & r128,
+                            BinOp::BitOr => l128 | r128,
+                            BinOp::BitXor => l128 ^ r128,
+                            _ => unreachable!(),
+                        };
+                        let r = r.to_le_bytes();
+                        for &k in &r[lc.len()..] {
+                            if k != 0 && (k != 255 || !is_signed) {
+                                return Err(MirEvalError::Panic);
+                            }
+                        }
+                        Owned(r[0..lc.len()].into())
+                    }
+                    BinOp::Shl | BinOp::Shr => {
+                        let shift_amout = if r128 < 0 {
+                            return Err(MirEvalError::Panic);
+                        } else if r128 > 128 {
+                            return Err(MirEvalError::Panic);
+                        } else {
+                            r128 as u8
+                        };
+                        let r = match op {
+                            BinOp::Shl => l128 << shift_amout,
+                            BinOp::Shr => l128 >> shift_amout,
+                            _ => unreachable!(),
+                        };
+                        Owned(r.to_le_bytes()[0..lc.len()].into())
+                    }
+                    BinOp::Offset => not_supported!("offset binop"),
+                }
+            }
+            Rvalue::Discriminant(p) => {
+                let ty = self.place_ty(p, locals)?;
+                let bytes = self.eval_place(p, locals)?.get(&self)?;
+                let layout = self.layout(&ty)?;
+                match layout.variants {
+                    Variants::Single { .. } => Owned(0u128.to_le_bytes().to_vec()),
+                    Variants::Multiple { tag, tag_encoding, .. } => {
+                        let Some(target_data_layout) = self.db.target_data_layout(self.crate_id) else {
+                            not_supported!("missing target data layout");
+                        };
+                        let size = tag.size(&*target_data_layout).bytes_usize();
+                        let offset = layout.fields.offset(0).bytes_usize(); // The only field on enum variants is the tag field
+                        match tag_encoding {
+                            TagEncoding::Direct => {
+                                let tag = &bytes[offset..offset + size];
+                                Owned(pad16(tag, false).to_vec())
+                            }
+                            TagEncoding::Niche { untagged_variant, niche_start, .. } => {
+                                let tag = &bytes[offset..offset + size];
+                                let candidate_discriminant = i128::from_le_bytes(pad16(tag, false))
+                                    .wrapping_sub(niche_start as i128);
+                                let enum_id = match ty.kind(Interner) {
+                                    TyKind::Adt(e, _) => match e.0 {
+                                        AdtId::EnumId(e) => e,
+                                        _ => not_supported!("Non enum with multi variant layout"),
+                                    },
+                                    _ => not_supported!("Non adt with multi variant layout"),
+                                };
+                                let enum_data = self.db.enum_data(enum_id);
+                                let result = 'b: {
+                                    for (local_id, _) in enum_data.variants.iter() {
+                                        if candidate_discriminant
+                                            == self.db.const_eval_discriminant(EnumVariantId {
+                                                parent: enum_id,
+                                                local_id,
+                                            })?
+                                        {
+                                            break 'b candidate_discriminant;
+                                        }
+                                    }
+                                    self.db.const_eval_discriminant(EnumVariantId {
+                                        parent: enum_id,
+                                        local_id: untagged_variant.0,
+                                    })?
+                                };
+                                Owned(result.to_le_bytes().to_vec())
+                            }
+                        }
+                    }
+                }
+            }
+            Rvalue::ShallowInitBox(_, _) => not_supported!("shallow init box"),
+            Rvalue::CopyForDeref(_) => not_supported!("copy for deref"),
+            Rvalue::Aggregate(kind, values) => match kind {
+                AggregateKind::Array(_) => {
+                    let mut r = vec![];
+                    for x in values {
+                        let value = self.eval_operand(x, locals)?.get(&self)?;
+                        r.extend(value);
+                    }
+                    Owned(r)
+                }
+                AggregateKind::Tuple(ty) => {
+                    let layout = self.layout(&ty)?;
+                    Owned(self.make_by_layout(
+                        layout.size.bytes_usize(),
+                        &layout,
+                        None,
+                        values,
+                        locals,
+                    )?)
+                }
+                AggregateKind::Union(x, f) => {
+                    let layout = self.layout_adt((*x).into(), Substitution::empty(Interner))?;
+                    let offset = layout
+                        .fields
+                        .offset(u32::from(f.local_id.into_raw()) as usize)
+                        .bytes_usize();
+                    let op = self.eval_operand(&values[0], locals)?.get(&self)?;
+                    let mut result = vec![0; layout.size.bytes_usize()];
+                    result[offset..offset + op.len()].copy_from_slice(op);
+                    Owned(result)
+                }
+                AggregateKind::Adt(x, subst) => {
+                    let (size, variant_layout, tag) =
+                        self.layout_of_variant(*x, subst.clone(), locals)?;
+                    Owned(self.make_by_layout(size, &variant_layout, tag, values, locals)?)
+                }
+            },
+            Rvalue::Cast(kind, operand, target_ty) => match kind {
+                CastKind::PointerExposeAddress => not_supported!("exposing pointer address"),
+                CastKind::PointerFromExposedAddress => {
+                    not_supported!("creating pointer from exposed address")
+                }
+                CastKind::Pointer(cast) => match cast {
+                    PointerCast::Unsize => {
+                        let current_ty = self.operand_ty(operand, locals)?;
+                        match &target_ty.data(Interner).kind {
+                            TyKind::Raw(_, ty) | TyKind::Ref(_, _, ty) => {
+                                match &ty.data(Interner).kind {
+                                    TyKind::Slice(_) => match &current_ty.data(Interner).kind {
+                                        TyKind::Raw(_, ty) | TyKind::Ref(_, _, ty) => {
+                                            match &ty.data(Interner).kind {
+                                                TyKind::Array(_, size) => {
+                                                    let addr = self
+                                                        .eval_operand(operand, locals)?
+                                                        .get(&self)?;
+                                                    let len = const_as_usize(size);
+                                                    let mut r = Vec::with_capacity(16);
+                                                    r.extend(addr.iter().copied());
+                                                    r.extend(len.to_le_bytes().into_iter());
+                                                    Owned(r)
+                                                }
+                                                _ => {
+                                                    not_supported!("slice unsizing from non arrays")
+                                                }
+                                            }
+                                        }
+                                        _ => not_supported!("slice unsizing from non pointers"),
+                                    },
+                                    TyKind::Dyn(_) => not_supported!("dyn pointer unsize cast"),
+                                    _ => not_supported!("unknown unsized cast"),
+                                }
+                            }
+                            _ => not_supported!("unsized cast on unknown pointer type"),
+                        }
+                    }
+                    x => not_supported!("pointer cast {x:?}"),
+                },
+                CastKind::DynStar => not_supported!("dyn star cast"),
+                CastKind::IntToInt => {
+                    // FIXME: handle signed cast
+                    let current = pad16(self.eval_operand(operand, locals)?.get(&self)?, false);
+                    let dest_size =
+                        self.size_of_sized(target_ty, locals, "destination of int to int cast")?;
+                    Owned(current[0..dest_size].to_vec())
+                }
+                CastKind::FloatToInt => not_supported!("float to int cast"),
+                CastKind::FloatToFloat => not_supported!("float to float cast"),
+                CastKind::IntToFloat => not_supported!("float to int cast"),
+                CastKind::PtrToPtr => not_supported!("ptr to ptr cast"),
+                CastKind::FnPtrToPtr => not_supported!("fn ptr to ptr cast"),
+            },
+        })
+    }
+
+    fn layout_of_variant(
+        &mut self,
+        x: VariantId,
+        subst: Substitution,
+        locals: &Locals<'_>,
+    ) -> Result<(usize, Layout, Option<(usize, usize, i128)>)> {
+        let adt = x.adt_id();
+        if let DefWithBodyId::VariantId(f) = locals.body.owner {
+            if let VariantId::EnumVariantId(x) = x {
+                if AdtId::from(f.parent) == adt {
+                    // Computing the exact size of enums require resolving the enum discriminants. In order to prevent loops (and
+                    // infinite sized type errors) we use a dummy layout
+                    let i = self.db.const_eval_discriminant(x)?;
+                    return Ok((16, self.layout(&TyBuilder::unit())?, Some((0, 16, i))));
+                }
+            }
+        }
+        let layout = self.layout_adt(adt, subst)?;
+        Ok(match layout.variants {
+            Variants::Single { .. } => (layout.size.bytes_usize(), layout, None),
+            Variants::Multiple { variants, tag, tag_encoding, .. } => {
+                let cx = self
+                    .db
+                    .target_data_layout(self.crate_id)
+                    .ok_or(MirEvalError::TargetDataLayoutNotAvailable)?;
+                let enum_variant_id = match x {
+                    VariantId::EnumVariantId(x) => x,
+                    _ => not_supported!("multi variant layout for non-enums"),
+                };
+                let rustc_enum_variant_idx = RustcEnumVariantIdx(enum_variant_id.local_id);
+                let mut discriminant = self.db.const_eval_discriminant(enum_variant_id)?;
+                let variant_layout = variants[rustc_enum_variant_idx].clone();
+                let have_tag = match tag_encoding {
+                    TagEncoding::Direct => true,
+                    TagEncoding::Niche { untagged_variant, niche_variants: _, niche_start } => {
+                        discriminant = discriminant.wrapping_add(niche_start as i128);
+                        untagged_variant != rustc_enum_variant_idx
+                    }
+                };
+                (
+                    layout.size.bytes_usize(),
+                    variant_layout,
+                    if have_tag {
+                        Some((
+                            layout.fields.offset(0).bytes_usize(),
+                            tag.size(&*cx).bytes_usize(),
+                            discriminant,
+                        ))
+                    } else {
+                        None
+                    },
+                )
+            }
+        })
+    }
+
+    fn make_by_layout(
+        &mut self,
+        size: usize, // Not neccessarily equal to variant_layout.size
+        variant_layout: &Layout,
+        tag: Option<(usize, usize, i128)>,
+        values: &Vec<Operand>,
+        locals: &Locals<'_>,
+    ) -> Result<Vec<u8>> {
+        let mut result = vec![0; size];
+        if let Some((offset, size, value)) = tag {
+            result[offset..offset + size].copy_from_slice(&value.to_le_bytes()[0..size]);
+        }
+        for (i, op) in values.iter().enumerate() {
+            let offset = variant_layout.fields.offset(i).bytes_usize();
+            let op = self.eval_operand(op, locals)?.get(&self)?;
+            result[offset..offset + op.len()].copy_from_slice(op);
+        }
+        Ok(result)
+    }
+
+    fn eval_operand(&mut self, x: &Operand, locals: &Locals<'_>) -> Result<Interval> {
+        Ok(match x {
+            Operand::Copy(p) | Operand::Move(p) => self.eval_place(p, locals)?,
+            Operand::Constant(konst) => {
+                let data = &konst.data(Interner);
+                match &data.value {
+                    chalk_ir::ConstValue::BoundVar(b) => {
+                        let c = locals
+                            .subst
+                            .as_slice(Interner)
+                            .get(b.index)
+                            .ok_or(MirEvalError::TypeError("missing generic arg"))?
+                            .assert_const_ref(Interner);
+                        self.eval_operand(&Operand::Constant(c.clone()), locals)?
+                    }
+                    chalk_ir::ConstValue::InferenceVar(_) => {
+                        not_supported!("inference var constant")
+                    }
+                    chalk_ir::ConstValue::Placeholder(_) => not_supported!("placeholder constant"),
+                    chalk_ir::ConstValue::Concrete(c) => match &c.interned {
+                        ConstScalar::Bytes(v, memory_map) => {
+                            let mut v: Cow<'_, [u8]> = Cow::Borrowed(v);
+                            let patch_map = memory_map.transform_addresses(|b| {
+                                let addr = self.heap_allocate(b.len());
+                                self.write_memory(addr, b)?;
+                                Ok(addr.to_usize())
+                            })?;
+                            let size = self.size_of(&data.ty, locals)?.unwrap_or(v.len());
+                            if size != v.len() {
+                                // Handle self enum
+                                if size == 16 && v.len() < 16 {
+                                    v = Cow::Owned(pad16(&v, false).to_vec());
+                                } else if size < 16 && v.len() == 16 {
+                                    v = Cow::Owned(v[0..size].to_vec());
+                                } else {
+                                    return Err(MirEvalError::InvalidConst(konst.clone()));
+                                }
+                            }
+                            let addr = self.heap_allocate(size);
+                            self.write_memory(addr, &v)?;
+                            self.patch_addresses(&patch_map, addr, &data.ty, locals)?;
+                            Interval::new(addr, size)
+                        }
+                        ConstScalar::Unknown => not_supported!("evaluating unknown const"),
+                    },
+                }
+            }
+        })
+    }
+
+    fn eval_place(&mut self, p: &Place, locals: &Locals<'_>) -> Result<Interval> {
+        let addr = self.place_addr(p, locals)?;
+        Ok(Interval::new(
+            addr,
+            self.size_of_sized(&self.place_ty(p, locals)?, locals, "type of this place")?,
+        ))
+    }
+
+    fn read_memory(&self, addr: Address, size: usize) -> Result<&[u8]> {
+        let (mem, pos) = match addr {
+            Stack(x) => (&self.stack, x),
+            Heap(x) => (&self.heap, x),
+        };
+        mem.get(pos..pos + size).ok_or(MirEvalError::UndefinedBehavior("out of bound memory read"))
+    }
+
+    fn write_memory(&mut self, addr: Address, r: &[u8]) -> Result<()> {
+        let (mem, pos) = match addr {
+            Stack(x) => (&mut self.stack, x),
+            Heap(x) => (&mut self.heap, x),
+        };
+        mem.get_mut(pos..pos + r.len())
+            .ok_or(MirEvalError::UndefinedBehavior("out of bound memory write"))?
+            .copy_from_slice(r);
+        Ok(())
+    }
+
+    fn size_of(&self, ty: &Ty, locals: &Locals<'_>) -> Result<Option<usize>> {
+        if let DefWithBodyId::VariantId(f) = locals.body.owner {
+            if let Some((adt, _)) = ty.as_adt() {
+                if AdtId::from(f.parent) == adt {
+                    // Computing the exact size of enums require resolving the enum discriminants. In order to prevent loops (and
+                    // infinite sized type errors) we use a dummy size
+                    return Ok(Some(16));
+                }
+            }
+        }
+        let ty = &self.ty_filler(ty, locals.subst, locals.body.owner)?;
+        let layout = self.layout(ty);
+        if self.assert_placeholder_ty_is_unused {
+            if matches!(layout, Err(MirEvalError::LayoutError(LayoutError::HasPlaceholder, _))) {
+                return Ok(Some(0));
+            }
+        }
+        let layout = layout?;
+        Ok(layout.is_sized().then(|| layout.size.bytes_usize()))
+    }
+
+    /// A version of `self.size_of` which returns error if the type is unsized. `what` argument should
+    /// be something that complete this: `error: type {ty} was unsized. {what} should be sized`
+    fn size_of_sized(&self, ty: &Ty, locals: &Locals<'_>, what: &'static str) -> Result<usize> {
+        match self.size_of(ty, locals)? {
+            Some(x) => Ok(x),
+            None => Err(MirEvalError::TypeIsUnsized(ty.clone(), what)),
+        }
+    }
+
+    /// Uses `ty_filler` to fill an entire subst
+    fn subst_filler(&self, subst: &Substitution, locals: &Locals<'_>) -> Substitution {
+        Substitution::from_iter(
+            Interner,
+            subst.iter(Interner).map(|x| match x.data(Interner) {
+                chalk_ir::GenericArgData::Ty(ty) => {
+                    let Ok(ty) = self.ty_filler(ty, locals.subst, locals.body.owner) else {
+                        return x.clone();
+                    };
+                    chalk_ir::GenericArgData::Ty(ty).intern(Interner)
+                }
+                _ => x.clone(),
+            }),
+        )
+    }
+
+    /// This function substitutes placeholders of the body with the provided subst, effectively plays
+    /// the rule of monomorphization. In addition to placeholders, it substitutes opaque types (return
+    /// position impl traits) with their underlying type.
+    fn ty_filler(&self, ty: &Ty, subst: &Substitution, owner: DefWithBodyId) -> Result<Ty> {
+        struct Filler<'a> {
+            db: &'a dyn HirDatabase,
+            subst: &'a Substitution,
+            skip_params: usize,
+        }
+        impl FallibleTypeFolder<Interner> for Filler<'_> {
+            type Error = MirEvalError;
+
+            fn as_dyn(&mut self) -> &mut dyn FallibleTypeFolder<Interner, Error = Self::Error> {
+                self
+            }
+
+            fn interner(&self) -> Interner {
+                Interner
+            }
+
+            fn try_fold_ty(
+                &mut self,
+                ty: Ty,
+                outer_binder: DebruijnIndex,
+            ) -> std::result::Result<Ty, Self::Error> {
+                match ty.kind(Interner) {
+                    TyKind::OpaqueType(id, subst) => {
+                        let impl_trait_id = self.db.lookup_intern_impl_trait_id((*id).into());
+                        match impl_trait_id {
+                            crate::ImplTraitId::ReturnTypeImplTrait(func, idx) => {
+                                let infer = self.db.infer(func.into());
+                                let filler = &mut Filler { db: self.db, subst, skip_params: 0 };
+                                filler.try_fold_ty(infer.type_of_rpit[idx].clone(), outer_binder)
+                            }
+                            crate::ImplTraitId::AsyncBlockTypeImplTrait(_, _) => {
+                                not_supported!("async block impl trait");
+                            }
+                        }
+                    }
+                    _ => ty.try_super_fold_with(self.as_dyn(), outer_binder),
+                }
+            }
+
+            fn try_fold_free_placeholder_ty(
+                &mut self,
+                idx: chalk_ir::PlaceholderIndex,
+                _outer_binder: DebruijnIndex,
+            ) -> std::result::Result<Ty, Self::Error> {
+                let x = from_placeholder_idx(self.db, idx);
+                Ok(self
+                    .subst
+                    .as_slice(Interner)
+                    .get((u32::from(x.local_id.into_raw()) as usize) + self.skip_params)
+                    .and_then(|x| x.ty(Interner))
+                    .ok_or(MirEvalError::TypeError("Generic arg not provided"))?
+                    .clone())
+            }
+        }
+        let filler = &mut Filler { db: self.db, subst, skip_params: 0 };
+        Ok(normalize(self.db, owner, ty.clone().try_fold_with(filler, DebruijnIndex::INNERMOST)?))
+    }
+
+    fn heap_allocate(&mut self, s: usize) -> Address {
+        let pos = self.heap.len();
+        self.heap.extend(iter::repeat(0).take(s));
+        Address::Heap(pos)
+    }
+
+    pub fn interpret_mir_with_no_arg(&mut self, body: &MirBody) -> Result<Vec<u8>> {
+        self.interpret_mir(&body, vec![].into_iter(), Substitution::empty(Interner))
+    }
+
+    fn detect_lang_function(&self, def: FunctionId) -> Option<LangItem> {
+        let candidate = lang_attr(self.db.upcast(), def)?;
+        // filter normal lang functions out
+        if [LangItem::IntoIterIntoIter, LangItem::IteratorNext].contains(&candidate) {
+            return None;
+        }
+        Some(candidate)
+    }
+
+    fn create_memory_map(&self, bytes: &[u8], ty: &Ty, locals: &Locals<'_>) -> Result<MemoryMap> {
+        // FIXME: support indirect references
+        let mut mm = MemoryMap::default();
+        match ty.kind(Interner) {
+            TyKind::Ref(_, _, t) => {
+                let size = self.size_of(t, locals)?;
+                match size {
+                    Some(size) => {
+                        let addr_usize = from_bytes!(usize, bytes);
+                        mm.insert(
+                            addr_usize,
+                            self.read_memory(Address::from_usize(addr_usize), size)?.to_vec(),
+                        )
+                    }
+                    None => {
+                        let element_size = match t.kind(Interner) {
+                            TyKind::Str => 1,
+                            TyKind::Slice(t) => {
+                                self.size_of_sized(t, locals, "slice inner type")?
+                            }
+                            _ => return Ok(mm), // FIXME: support other kind of unsized types
+                        };
+                        let (addr, meta) = bytes.split_at(bytes.len() / 2);
+                        let size = element_size * from_bytes!(usize, meta);
+                        let addr = Address::from_bytes(addr)?;
+                        mm.insert(addr.to_usize(), self.read_memory(addr, size)?.to_vec());
+                    }
+                }
+            }
+            _ => (),
+        }
+        Ok(mm)
+    }
+
+    fn patch_addresses(
+        &mut self,
+        patch_map: &HashMap<usize, usize>,
+        addr: Address,
+        ty: &Ty,
+        locals: &Locals<'_>,
+    ) -> Result<()> {
+        // FIXME: support indirect references
+        let my_size = self.size_of_sized(ty, locals, "value to patch address")?;
+        match ty.kind(Interner) {
+            TyKind::Ref(_, _, t) => {
+                let size = self.size_of(t, locals)?;
+                match size {
+                    Some(_) => {
+                        let current = from_bytes!(usize, self.read_memory(addr, my_size)?);
+                        if let Some(x) = patch_map.get(&current) {
+                            self.write_memory(addr, &x.to_le_bytes())?;
+                        }
+                    }
+                    None => {
+                        let current = from_bytes!(usize, self.read_memory(addr, my_size / 2)?);
+                        if let Some(x) = patch_map.get(&current) {
+                            self.write_memory(addr, &x.to_le_bytes())?;
+                        }
+                    }
+                }
+            }
+            _ => (),
+        }
+        Ok(())
+    }
+
+    fn exec_intrinsic(
+        &self,
+        as_str: &str,
+        _arg_bytes: impl Iterator<Item = Vec<u8>>,
+        generic_args: Substitution,
+        locals: &Locals<'_>,
+    ) -> Result<Vec<u8>> {
+        match as_str {
+            "size_of" => {
+                let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else {
+                    return Err(MirEvalError::TypeError("size_of generic arg is not provided"));
+                };
+                let size = self.size_of(ty, locals)?;
+                match size {
+                    Some(x) => Ok(x.to_le_bytes().to_vec()),
+                    None => return Err(MirEvalError::TypeError("size_of arg is unsized")),
+                }
+            }
+            _ => not_supported!("unknown intrinsic {as_str}"),
+        }
+    }
+
+    pub(crate) fn exec_lang_item(
+        &self,
+        x: LangItem,
+        mut args: std::vec::IntoIter<Vec<u8>>,
+    ) -> Result<Vec<u8>> {
+        use LangItem::*;
+        match x {
+            PanicFmt | BeginPanic => Err(MirEvalError::Panic),
+            SliceLen => {
+                let arg = args
+                    .next()
+                    .ok_or(MirEvalError::TypeError("argument of <[T]>::len() is not provided"))?;
+                let ptr_size = arg.len() / 2;
+                Ok(arg[ptr_size..].into())
+            }
+            x => not_supported!("Executing lang item {x:?}"),
+        }
+    }
+}
+
+pub fn pad16(x: &[u8], is_signed: bool) -> [u8; 16] {
+    let is_negative = is_signed && x.last().unwrap_or(&0) > &128;
+    let fill_with = if is_negative { 255 } else { 0 };
+    x.iter()
+        .copied()
+        .chain(iter::repeat(fill_with))
+        .take(16)
+        .collect::<Vec<u8>>()
+        .try_into()
+        .expect("iterator take is not working")
+}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs
new file mode 100644
index 00000000000..435a914088b
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs
@@ -0,0 +1,1577 @@
+//! This module generates a polymorphic MIR from a hir body
+
+use std::{iter, mem, sync::Arc};
+
+use chalk_ir::{BoundVar, ConstData, DebruijnIndex, TyKind};
+use hir_def::{
+    body::Body,
+    expr::{
+        Array, BindingAnnotation, BindingId, ExprId, LabelId, Literal, MatchArm, Pat, PatId,
+        RecordLitField,
+    },
+    lang_item::{LangItem, LangItemTarget},
+    layout::LayoutError,
+    path::Path,
+    resolver::{resolver_for_expr, ResolveValueResult, ValueNs},
+    DefWithBodyId, EnumVariantId, HasModule,
+};
+use hir_expand::name::Name;
+use la_arena::ArenaMap;
+
+use crate::{
+    consteval::ConstEvalError, db::HirDatabase, display::HirDisplay, infer::TypeMismatch,
+    inhabitedness::is_ty_uninhabited_from, layout::layout_of_ty, mapping::ToChalk, static_lifetime,
+    utils::generics, Adjust, Adjustment, AutoBorrow, CallableDefId, TyBuilder, TyExt,
+};
+
+use super::*;
+
+mod as_place;
+
+#[derive(Debug, Clone, Copy)]
+struct LoopBlocks {
+    begin: BasicBlockId,
+    /// `None` for loops that are not terminating
+    end: Option<BasicBlockId>,
+}
+
+struct MirLowerCtx<'a> {
+    result: MirBody,
+    owner: DefWithBodyId,
+    current_loop_blocks: Option<LoopBlocks>,
+    discr_temp: Option<Place>,
+    db: &'a dyn HirDatabase,
+    body: &'a Body,
+    infer: &'a InferenceResult,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum MirLowerError {
+    ConstEvalError(Box<ConstEvalError>),
+    LayoutError(LayoutError),
+    IncompleteExpr,
+    UnresolvedName(String),
+    RecordLiteralWithoutPath,
+    UnresolvedMethod,
+    UnresolvedField,
+    MissingFunctionDefinition,
+    TypeMismatch(TypeMismatch),
+    /// This should be never happen. Type mismatch should catch everything.
+    TypeError(&'static str),
+    NotSupported(String),
+    ContinueWithoutLoop,
+    BreakWithoutLoop,
+    Loop,
+    /// Something that should never happen and is definitely a bug, but we don't want to panic if it happened
+    ImplementationError(&'static str),
+    LangItemNotFound(LangItem),
+    MutatingRvalue,
+}
+
+macro_rules! not_supported {
+    ($x: expr) => {
+        return Err(MirLowerError::NotSupported(format!($x)))
+    };
+}
+
+macro_rules! implementation_error {
+    ($x: expr) => {{
+        ::stdx::never!("MIR lower implementation bug: {}", $x);
+        return Err(MirLowerError::ImplementationError($x));
+    }};
+}
+
+impl From<ConstEvalError> for MirLowerError {
+    fn from(value: ConstEvalError) -> Self {
+        match value {
+            ConstEvalError::MirLowerError(e) => e,
+            _ => MirLowerError::ConstEvalError(Box::new(value)),
+        }
+    }
+}
+
+impl From<LayoutError> for MirLowerError {
+    fn from(value: LayoutError) -> Self {
+        MirLowerError::LayoutError(value)
+    }
+}
+
+impl MirLowerError {
+    fn unresolved_path(db: &dyn HirDatabase, p: &Path) -> Self {
+        Self::UnresolvedName(p.display(db).to_string())
+    }
+}
+
+type Result<T> = std::result::Result<T, MirLowerError>;
+
+impl MirLowerCtx<'_> {
+    fn temp(&mut self, ty: Ty) -> Result<LocalId> {
+        if matches!(ty.kind(Interner), TyKind::Slice(_) | TyKind::Dyn(_)) {
+            implementation_error!("unsized temporaries");
+        }
+        Ok(self.result.locals.alloc(Local { ty }))
+    }
+
+    fn lower_expr_to_some_operand(
+        &mut self,
+        expr_id: ExprId,
+        current: BasicBlockId,
+    ) -> Result<Option<(Operand, BasicBlockId)>> {
+        if !self.has_adjustments(expr_id) {
+            match &self.body.exprs[expr_id] {
+                Expr::Literal(l) => {
+                    let ty = self.expr_ty(expr_id);
+                    return Ok(Some((self.lower_literal_to_operand(ty, l)?, current)));
+                }
+                _ => (),
+            }
+        }
+        let Some((p, current)) = self.lower_expr_as_place(current, expr_id, true)? else {
+            return Ok(None);
+        };
+        Ok(Some((Operand::Copy(p), current)))
+    }
+
+    fn lower_expr_to_place_with_adjust(
+        &mut self,
+        expr_id: ExprId,
+        place: Place,
+        current: BasicBlockId,
+        adjustments: &[Adjustment],
+    ) -> Result<Option<BasicBlockId>> {
+        match adjustments.split_last() {
+            Some((last, rest)) => match &last.kind {
+                Adjust::NeverToAny => {
+                    let temp = self.temp(TyKind::Never.intern(Interner))?;
+                    self.lower_expr_to_place_with_adjust(expr_id, temp.into(), current, rest)
+                }
+                Adjust::Deref(_) => {
+                    let Some((p, current)) = self.lower_expr_as_place_with_adjust(current, expr_id, true, adjustments)? else {
+                            return Ok(None);
+                        };
+                    self.push_assignment(current, place, Operand::Copy(p).into(), expr_id.into());
+                    Ok(Some(current))
+                }
+                Adjust::Borrow(AutoBorrow::Ref(m) | AutoBorrow::RawPtr(m)) => {
+                    let Some((p, current)) = self.lower_expr_as_place_with_adjust(current, expr_id, true, rest)? else {
+                            return Ok(None);
+                        };
+                    let bk = BorrowKind::from_chalk(*m);
+                    self.push_assignment(current, place, Rvalue::Ref(bk, p), expr_id.into());
+                    Ok(Some(current))
+                }
+                Adjust::Pointer(cast) => {
+                    let Some((p, current)) = self.lower_expr_as_place_with_adjust(current, expr_id, true, rest)? else {
+                            return Ok(None);
+                        };
+                    self.push_assignment(
+                        current,
+                        place,
+                        Rvalue::Cast(
+                            CastKind::Pointer(cast.clone()),
+                            Operand::Copy(p).into(),
+                            last.target.clone(),
+                        ),
+                        expr_id.into(),
+                    );
+                    Ok(Some(current))
+                }
+            },
+            None => self.lower_expr_to_place_without_adjust(expr_id, place, current),
+        }
+    }
+
+    fn lower_expr_to_place(
+        &mut self,
+        expr_id: ExprId,
+        place: Place,
+        prev_block: BasicBlockId,
+    ) -> Result<Option<BasicBlockId>> {
+        if let Some(adjustments) = self.infer.expr_adjustments.get(&expr_id) {
+            return self.lower_expr_to_place_with_adjust(expr_id, place, prev_block, adjustments);
+        }
+        self.lower_expr_to_place_without_adjust(expr_id, place, prev_block)
+    }
+
+    fn lower_expr_to_place_without_adjust(
+        &mut self,
+        expr_id: ExprId,
+        place: Place,
+        mut current: BasicBlockId,
+    ) -> Result<Option<BasicBlockId>> {
+        match &self.body.exprs[expr_id] {
+            Expr::Missing => Err(MirLowerError::IncompleteExpr),
+            Expr::Path(p) => {
+                let unresolved_name = || MirLowerError::unresolved_path(self.db, p);
+                let resolver = resolver_for_expr(self.db.upcast(), self.owner, expr_id);
+                let pr = resolver
+                    .resolve_path_in_value_ns(self.db.upcast(), p.mod_path())
+                    .ok_or_else(unresolved_name)?;
+                let pr = match pr {
+                    ResolveValueResult::ValueNs(v) => v,
+                    ResolveValueResult::Partial(..) => {
+                        if let Some(assoc) = self
+                            .infer
+                            .assoc_resolutions_for_expr(expr_id)
+                        {
+                            match assoc.0 {
+                                hir_def::AssocItemId::ConstId(c) => {
+                                    self.lower_const(c, current, place, expr_id.into())?;
+                                    return Ok(Some(current))
+                                },
+                                _ => not_supported!("associated functions and types"),
+                            }
+                        } else if let Some(variant) = self
+                            .infer
+                            .variant_resolution_for_expr(expr_id)
+                        {
+                            match variant {
+                                VariantId::EnumVariantId(e) => ValueNs::EnumVariantId(e),
+                                VariantId::StructId(s) => ValueNs::StructId(s),
+                                VariantId::UnionId(_) => implementation_error!("Union variant as path"),
+                            }
+                        } else {
+                            return Err(unresolved_name());
+                        }
+                    }
+                };
+                match pr {
+                    ValueNs::LocalBinding(pat_id) => {
+                        self.push_assignment(
+                            current,
+                            place,
+                            Operand::Copy(self.result.binding_locals[pat_id].into()).into(),
+                            expr_id.into(),
+                        );
+                        Ok(Some(current))
+                    }
+                    ValueNs::ConstId(const_id) => {
+                        self.lower_const(const_id, current, place, expr_id.into())?;
+                        Ok(Some(current))
+                    }
+                    ValueNs::EnumVariantId(variant_id) => {
+                        let ty = self.infer.type_of_expr[expr_id].clone();
+                        let current = self.lower_enum_variant(
+                            variant_id,
+                            current,
+                            place,
+                            ty,
+                            vec![],
+                            expr_id.into(),
+                        )?;
+                        Ok(Some(current))
+                    }
+                    ValueNs::GenericParam(p) => {
+                        let Some(def) = self.owner.as_generic_def_id() else {
+                            not_supported!("owner without generic def id");
+                        };
+                        let gen = generics(self.db.upcast(), def);
+                        let ty = self.expr_ty(expr_id);
+                        self.push_assignment(
+                            current,
+                            place,
+                            Operand::Constant(
+                                ConstData {
+                                    ty,
+                                    value: chalk_ir::ConstValue::BoundVar(BoundVar::new(
+                                        DebruijnIndex::INNERMOST,
+                                        gen.param_idx(p.into()).ok_or(MirLowerError::TypeError(
+                                            "fail to lower const generic param",
+                                        ))?,
+                                    )),
+                                }
+                                .intern(Interner),
+                            )
+                            .into(),
+                            expr_id.into(),
+                        );
+                        Ok(Some(current))
+                    }
+                    ValueNs::StructId(_) => {
+                        // It's probably a unit struct or a zero sized function, so no action is needed.
+                        Ok(Some(current))
+                    }
+                    x => {
+                        not_supported!("unknown name {x:?} in value name space");
+                    }
+                }
+            }
+            Expr::If { condition, then_branch, else_branch } => {
+                let Some((discr, current)) = self.lower_expr_to_some_operand(*condition, current)? else {
+                    return Ok(None);
+                };
+                let start_of_then = self.new_basic_block();
+                let end_of_then =
+                    self.lower_expr_to_place(*then_branch, place.clone(), start_of_then)?;
+                let start_of_else = self.new_basic_block();
+                let end_of_else = if let Some(else_branch) = else_branch {
+                    self.lower_expr_to_place(*else_branch, place, start_of_else)?
+                } else {
+                    Some(start_of_else)
+                };
+                self.set_terminator(
+                    current,
+                    Terminator::SwitchInt {
+                        discr,
+                        targets: SwitchTargets::static_if(1, start_of_then, start_of_else),
+                    },
+                );
+                Ok(self.merge_blocks(end_of_then, end_of_else))
+            }
+            Expr::Let { pat, expr } => {
+                let Some((cond_place, current)) = self.lower_expr_as_place(current, *expr, true)? else {
+                    return Ok(None);
+                };
+                let (then_target, else_target) = self.pattern_match(
+                    current,
+                    None,
+                    cond_place,
+                    self.expr_ty_after_adjustments(*expr),
+                    *pat,
+                    BindingAnnotation::Unannotated,
+                )?;
+                self.write_bytes_to_place(
+                    then_target,
+                    place.clone(),
+                    vec![1],
+                    TyBuilder::bool(),
+                    MirSpan::Unknown,
+                )?;
+                if let Some(else_target) = else_target {
+                    self.write_bytes_to_place(
+                        else_target,
+                        place,
+                        vec![0],
+                        TyBuilder::bool(),
+                        MirSpan::Unknown,
+                    )?;
+                }
+                Ok(self.merge_blocks(Some(then_target), else_target))
+            }
+            Expr::Unsafe { id: _, statements, tail } => {
+                self.lower_block_to_place(None, statements, current, *tail, place)
+            }
+            Expr::Block { id: _, statements, tail, label } => {
+                self.lower_block_to_place(*label, statements, current, *tail, place)
+            }
+            Expr::Loop { body, label } => self.lower_loop(current, *label, |this, begin| {
+                if let Some((_, block)) = this.lower_expr_as_place(begin, *body, true)? {
+                    this.set_goto(block, begin);
+                }
+                Ok(())
+            }),
+            Expr::While { condition, body, label } => {
+                self.lower_loop(current, *label, |this, begin| {
+                    let Some((discr, to_switch)) = this.lower_expr_to_some_operand(*condition, begin)? else {
+                        return Ok(());
+                    };
+                    let end = this.current_loop_end()?;
+                    let after_cond = this.new_basic_block();
+                    this.set_terminator(
+                        to_switch,
+                        Terminator::SwitchInt {
+                            discr,
+                            targets: SwitchTargets::static_if(1, after_cond, end),
+                        },
+                    );
+                    if let Some((_, block)) = this.lower_expr_as_place(after_cond, *body, true)? {
+                        this.set_goto(block, begin);
+                    }
+                    Ok(())
+                })
+            }
+            &Expr::For { iterable, pat, body, label } => {
+                let into_iter_fn = self.resolve_lang_item(LangItem::IntoIterIntoIter)?
+                    .as_function().ok_or(MirLowerError::LangItemNotFound(LangItem::IntoIterIntoIter))?;
+                let iter_next_fn = self.resolve_lang_item(LangItem::IteratorNext)?
+                    .as_function().ok_or(MirLowerError::LangItemNotFound(LangItem::IteratorNext))?;
+                let option_some = self.resolve_lang_item(LangItem::OptionSome)?
+                    .as_enum_variant().ok_or(MirLowerError::LangItemNotFound(LangItem::OptionSome))?;
+                let option = option_some.parent;
+                let into_iter_fn_op = Operand::const_zst(
+                    TyKind::FnDef(
+                        self.db.intern_callable_def(CallableDefId::FunctionId(into_iter_fn)).into(),
+                        Substitution::from1(Interner, self.expr_ty(iterable))
+                    ).intern(Interner));
+                let iter_next_fn_op = Operand::const_zst(
+                    TyKind::FnDef(
+                        self.db.intern_callable_def(CallableDefId::FunctionId(iter_next_fn)).into(),
+                        Substitution::from1(Interner, self.expr_ty(iterable))
+                    ).intern(Interner));
+                let &Some(iterator_ty) = &self.infer.type_of_for_iterator.get(&expr_id) else {
+                    return Err(MirLowerError::TypeError("unknown for loop iterator type"));
+                };
+                let ref_mut_iterator_ty = TyKind::Ref(Mutability::Mut, static_lifetime(), iterator_ty.clone()).intern(Interner);
+                let item_ty = &self.infer.type_of_pat[pat];
+                let option_item_ty = TyKind::Adt(chalk_ir::AdtId(option.into()), Substitution::from1(Interner, item_ty.clone())).intern(Interner);
+                let iterator_place: Place = self.temp(iterator_ty.clone())?.into();
+                let option_item_place: Place = self.temp(option_item_ty.clone())?.into();
+                let ref_mut_iterator_place: Place = self.temp(ref_mut_iterator_ty)?.into();
+                let Some(current) = self.lower_call_and_args(into_iter_fn_op, Some(iterable).into_iter(), iterator_place.clone(), current, false)?
+                else {
+                    return Ok(None);
+                };
+                self.push_assignment(current, ref_mut_iterator_place.clone(), Rvalue::Ref(BorrowKind::Mut { allow_two_phase_borrow: false }, iterator_place), expr_id.into());
+                self.lower_loop(current, label, |this, begin| {
+                    let Some(current) = this.lower_call(iter_next_fn_op, vec![Operand::Copy(ref_mut_iterator_place)], option_item_place.clone(), begin, false)?
+                    else {
+                        return Ok(());
+                    };
+                    let end = this.current_loop_end()?;
+                    let (current, _) = this.pattern_matching_variant(
+                        option_item_ty.clone(),
+                        BindingAnnotation::Unannotated,
+                        option_item_place.into(),
+                        option_some.into(),
+                        current,
+                        pat.into(),
+                        Some(end),
+                        &[pat], &None)?;
+                    if let Some((_, block)) = this.lower_expr_as_place(current, body, true)? {
+                        this.set_goto(block, begin);
+                    }
+                    Ok(())
+                })
+            },
+            Expr::Call { callee, args, .. } => {
+                let callee_ty = self.expr_ty_after_adjustments(*callee);
+                match &callee_ty.data(Interner).kind {
+                    chalk_ir::TyKind::FnDef(..) => {
+                        let func = Operand::from_bytes(vec![], callee_ty.clone());
+                        self.lower_call_and_args(func, args.iter().copied(), place, current, self.is_uninhabited(expr_id))
+                    }
+                    TyKind::Scalar(_)
+                    | TyKind::Tuple(_, _)
+                    | TyKind::Array(_, _)
+                    | TyKind::Adt(_, _)
+                    | TyKind::Str
+                    | TyKind::Foreign(_)
+                    | TyKind::Slice(_) => {
+                        return Err(MirLowerError::TypeError("function call on data type"))
+                    }
+                    TyKind::Error => return Err(MirLowerError::MissingFunctionDefinition),
+                    TyKind::AssociatedType(_, _)
+                    | TyKind::Raw(_, _)
+                    | TyKind::Ref(_, _, _)
+                    | TyKind::OpaqueType(_, _)
+                    | TyKind::Never
+                    | TyKind::Closure(_, _)
+                    | TyKind::Generator(_, _)
+                    | TyKind::GeneratorWitness(_, _)
+                    | TyKind::Placeholder(_)
+                    | TyKind::Dyn(_)
+                    | TyKind::Alias(_)
+                    | TyKind::Function(_)
+                    | TyKind::BoundVar(_)
+                    | TyKind::InferenceVar(_, _) => not_supported!("dynamic function call"),
+                }
+            }
+            Expr::MethodCall { receiver, args, .. } => {
+                let (func_id, generic_args) =
+                    self.infer.method_resolution(expr_id).ok_or(MirLowerError::UnresolvedMethod)?;
+                let ty = chalk_ir::TyKind::FnDef(
+                    CallableDefId::FunctionId(func_id).to_chalk(self.db),
+                    generic_args,
+                )
+                .intern(Interner);
+                let func = Operand::from_bytes(vec![], ty);
+                self.lower_call_and_args(
+                    func,
+                    iter::once(*receiver).chain(args.iter().copied()),
+                    place,
+                    current,
+                    self.is_uninhabited(expr_id),
+                )
+            }
+            Expr::Match { expr, arms } => {
+                let Some((cond_place, mut current)) = self.lower_expr_as_place(current, *expr, true)?
+                else {
+                    return Ok(None);
+                };
+                let cond_ty = self.expr_ty_after_adjustments(*expr);
+                let mut end = None;
+                for MatchArm { pat, guard, expr } in arms.iter() {
+                    if guard.is_some() {
+                        not_supported!("pattern matching with guard");
+                    }
+                    let (then, otherwise) = self.pattern_match(
+                        current,
+                        None,
+                        cond_place.clone(),
+                        cond_ty.clone(),
+                        *pat,
+                        BindingAnnotation::Unannotated,
+                    )?;
+                    if let Some(block) = self.lower_expr_to_place(*expr, place.clone(), then)? {
+                        let r = end.get_or_insert_with(|| self.new_basic_block());
+                        self.set_goto(block, *r);
+                    }
+                    match otherwise {
+                        Some(o) => current = o,
+                        None => {
+                            // The current pattern was irrefutable, so there is no need to generate code
+                            // for the rest of patterns
+                            break;
+                        }
+                    }
+                }
+                if self.is_unterminated(current) {
+                    self.set_terminator(current, Terminator::Unreachable);
+                }
+                Ok(end)
+            }
+            Expr::Continue { label } => match label {
+                Some(_) => not_supported!("continue with label"),
+                None => {
+                    let loop_data =
+                        self.current_loop_blocks.ok_or(MirLowerError::ContinueWithoutLoop)?;
+                    self.set_goto(current, loop_data.begin);
+                    Ok(None)
+                }
+            },
+            Expr::Break { expr, label } => {
+                if expr.is_some() {
+                    not_supported!("break with value");
+                }
+                match label {
+                    Some(_) => not_supported!("break with label"),
+                    None => {
+                        let end =
+                            self.current_loop_end()?;
+                        self.set_goto(current, end);
+                        Ok(None)
+                    }
+                }
+            }
+            Expr::Return { expr } => {
+                if let Some(expr) = expr {
+                    if let Some(c) = self.lower_expr_to_place(*expr, return_slot().into(), current)? {
+                        current = c;
+                    } else {
+                        return Ok(None);
+                    }
+                }
+                self.set_terminator(current, Terminator::Return);
+                Ok(None)
+            }
+            Expr::Yield { .. } => not_supported!("yield"),
+            Expr::RecordLit { fields, path, .. } => {
+                let variant_id = self
+                    .infer
+                    .variant_resolution_for_expr(expr_id)
+                    .ok_or_else(|| match path {
+                        Some(p) => MirLowerError::UnresolvedName(p.display(self.db).to_string()),
+                        None => MirLowerError::RecordLiteralWithoutPath,
+                    })?;
+                let subst = match self.expr_ty(expr_id).kind(Interner) {
+                    TyKind::Adt(_, s) => s.clone(),
+                    _ => not_supported!("Non ADT record literal"),
+                };
+                let variant_data = variant_id.variant_data(self.db.upcast());
+                match variant_id {
+                    VariantId::EnumVariantId(_) | VariantId::StructId(_) => {
+                        let mut operands = vec![None; variant_data.fields().len()];
+                        for RecordLitField { name, expr } in fields.iter() {
+                            let field_id =
+                                variant_data.field(name).ok_or(MirLowerError::UnresolvedField)?;
+                            let Some((op, c)) = self.lower_expr_to_some_operand(*expr, current)? else {
+                                return Ok(None);
+                            };
+                            current = c;
+                            operands[u32::from(field_id.into_raw()) as usize] = Some(op);
+                        }
+                        self.push_assignment(
+                            current,
+                            place,
+                            Rvalue::Aggregate(
+                                AggregateKind::Adt(variant_id, subst),
+                                operands.into_iter().map(|x| x).collect::<Option<_>>().ok_or(
+                                    MirLowerError::TypeError("missing field in record literal"),
+                                )?,
+                            ),
+                            expr_id.into(),
+                        );
+                        Ok(Some(current))
+                    }
+                    VariantId::UnionId(union_id) => {
+                        let [RecordLitField { name, expr }] = fields.as_ref() else {
+                            not_supported!("Union record literal with more than one field");
+                        };
+                        let local_id =
+                            variant_data.field(name).ok_or(MirLowerError::UnresolvedField)?;
+                        let mut place = place;
+                        place
+                            .projection
+                            .push(PlaceElem::Field(FieldId { parent: union_id.into(), local_id }));
+                        self.lower_expr_to_place(*expr, place, current)
+                    }
+                }
+            }
+            Expr::Await { .. } => not_supported!("await"),
+            Expr::Try { .. } => not_supported!("? operator"),
+            Expr::Yeet { .. } => not_supported!("yeet"),
+            Expr::TryBlock { .. } => not_supported!("try block"),
+            Expr::Async { .. } => not_supported!("async block"),
+            Expr::Const { .. } => not_supported!("anonymous const block"),
+            Expr::Cast { expr, type_ref: _ } => {
+                let Some((x, current)) = self.lower_expr_to_some_operand(*expr, current)? else {
+                    return Ok(None);
+                };
+                let source_ty = self.infer[*expr].clone();
+                let target_ty = self.infer[expr_id].clone();
+                self.push_assignment(
+                    current,
+                    place,
+                    Rvalue::Cast(cast_kind(&source_ty, &target_ty)?, x, target_ty),
+                    expr_id.into(),
+                );
+                Ok(Some(current))
+            }
+            Expr::Ref { expr, rawness: _, mutability } => {
+                let Some((p, current)) = self.lower_expr_as_place(current, *expr, true)? else {
+                    return Ok(None);
+                };
+                let bk = BorrowKind::from_hir(*mutability);
+                self.push_assignment(current, place, Rvalue::Ref(bk, p), expr_id.into());
+                Ok(Some(current))
+            }
+            Expr::Box { .. } => not_supported!("box expression"),
+            Expr::Field { .. } | Expr::Index { .. } | Expr::UnaryOp { op: hir_def::expr::UnaryOp::Deref, .. } => {
+                let Some((p, current)) = self.lower_expr_as_place_without_adjust(current, expr_id, true)? else {
+                    return Ok(None);
+                };
+                self.push_assignment(current, place, Operand::Copy(p).into(), expr_id.into());
+                Ok(Some(current))
+            }
+            Expr::UnaryOp { expr, op: op @ (hir_def::expr::UnaryOp::Not | hir_def::expr::UnaryOp::Neg) } => {
+                let Some((operand, current)) = self.lower_expr_to_some_operand(*expr, current)? else {
+                    return Ok(None);
+                };
+                let operation = match op {
+                    hir_def::expr::UnaryOp::Not => UnOp::Not,
+                    hir_def::expr::UnaryOp::Neg => UnOp::Neg,
+                    _ => unreachable!(),
+                };
+                self.push_assignment(
+                    current,
+                    place,
+                    Rvalue::UnaryOp(operation, operand),
+                    expr_id.into(),
+                );
+                Ok(Some(current))
+            },
+            Expr::BinaryOp { lhs, rhs, op } => {
+                let op = op.ok_or(MirLowerError::IncompleteExpr)?;
+                if let hir_def::expr::BinaryOp::Assignment { op } = op {
+                    if op.is_some() {
+                        not_supported!("assignment with arith op (like +=)");
+                    }
+                    let Some((lhs_place, current)) =
+                        self.lower_expr_as_place(current, *lhs, false)?
+                    else {
+                        return Ok(None);
+                    };
+                    let Some((rhs_op, current)) = self.lower_expr_to_some_operand(*rhs, current)? else {
+                        return Ok(None);
+                    };
+                    self.push_assignment(current, lhs_place, rhs_op.into(), expr_id.into());
+                    return Ok(Some(current));
+                }
+                let Some((lhs_op, current)) = self.lower_expr_to_some_operand(*lhs, current)? else {
+                    return Ok(None);
+                };
+                let Some((rhs_op, current)) = self.lower_expr_to_some_operand(*rhs, current)? else {
+                    return Ok(None);
+                };
+                self.push_assignment(
+                    current,
+                    place,
+                    Rvalue::CheckedBinaryOp(
+                        match op {
+                            hir_def::expr::BinaryOp::LogicOp(op) => match op {
+                                hir_def::expr::LogicOp::And => BinOp::BitAnd, // FIXME: make these short circuit
+                                hir_def::expr::LogicOp::Or => BinOp::BitOr,
+                            },
+                            hir_def::expr::BinaryOp::ArithOp(op) => BinOp::from(op),
+                            hir_def::expr::BinaryOp::CmpOp(op) => BinOp::from(op),
+                            hir_def::expr::BinaryOp::Assignment { .. } => unreachable!(), // handled above
+                        },
+                        lhs_op,
+                        rhs_op,
+                    ),
+                    expr_id.into(),
+                );
+                Ok(Some(current))
+            }
+            Expr::Range { .. } => not_supported!("range"),
+            Expr::Closure { .. } => not_supported!("closure"),
+            Expr::Tuple { exprs, is_assignee_expr: _ } => {
+                let Some(values) = exprs
+                        .iter()
+                        .map(|x| {
+                            let Some((o, c)) = self.lower_expr_to_some_operand(*x, current)? else {
+                                return Ok(None);
+                            };
+                            current = c;
+                            Ok(Some(o))
+                        })
+                        .collect::<Result<Option<_>>>()?
+                else {
+                    return Ok(None);
+                };
+                let r = Rvalue::Aggregate(
+                    AggregateKind::Tuple(self.expr_ty(expr_id)),
+                    values,
+                );
+                self.push_assignment(current, place, r, expr_id.into());
+                Ok(Some(current))
+            }
+            Expr::Array(l) => match l {
+                Array::ElementList { elements, .. } => {
+                    let elem_ty = match &self.expr_ty(expr_id).data(Interner).kind {
+                        TyKind::Array(ty, _) => ty.clone(),
+                        _ => {
+                            return Err(MirLowerError::TypeError(
+                                "Array expression with non array type",
+                            ))
+                        }
+                    };
+                    let Some(values) = elements
+                            .iter()
+                            .map(|x| {
+                                let Some((o, c)) = self.lower_expr_to_some_operand(*x, current)? else {
+                                    return Ok(None);
+                                };
+                                current = c;
+                                Ok(Some(o))
+                            })
+                            .collect::<Result<Option<_>>>()?
+                    else {
+                        return Ok(None);
+                    };
+                    let r = Rvalue::Aggregate(
+                        AggregateKind::Array(elem_ty),
+                        values,
+                    );
+                    self.push_assignment(current, place, r, expr_id.into());
+                    Ok(Some(current))
+                }
+                Array::Repeat { .. } => not_supported!("array repeat"),
+            },
+            Expr::Literal(l) => {
+                let ty = self.expr_ty(expr_id);
+                let op = self.lower_literal_to_operand(ty, l)?;
+                self.push_assignment(current, place, op.into(), expr_id.into());
+                Ok(Some(current))
+            }
+            Expr::Underscore => not_supported!("underscore"),
+        }
+    }
+
+    fn push_field_projection(&self, place: &mut Place, expr_id: ExprId) -> Result<()> {
+        if let Expr::Field { expr, name } = &self.body[expr_id] {
+            if let TyKind::Tuple(..) = self.expr_ty_after_adjustments(*expr).kind(Interner) {
+                let index = name
+                    .as_tuple_index()
+                    .ok_or(MirLowerError::TypeError("named field on tuple"))?;
+                place.projection.push(ProjectionElem::TupleField(index))
+            } else {
+                let field =
+                    self.infer.field_resolution(expr_id).ok_or(MirLowerError::UnresolvedField)?;
+                place.projection.push(ProjectionElem::Field(field));
+            }
+        } else {
+            not_supported!("")
+        }
+        Ok(())
+    }
+
+    fn lower_literal_to_operand(&mut self, ty: Ty, l: &Literal) -> Result<Operand> {
+        let size = layout_of_ty(self.db, &ty, self.owner.module(self.db.upcast()).krate())?
+            .size
+            .bytes_usize();
+        let bytes = match l {
+            hir_def::expr::Literal::String(b) => {
+                let b = b.as_bytes();
+                let mut data = vec![];
+                data.extend(0usize.to_le_bytes());
+                data.extend(b.len().to_le_bytes());
+                let mut mm = MemoryMap::default();
+                mm.insert(0, b.to_vec());
+                return Ok(Operand::from_concrete_const(data, mm, ty));
+            }
+            hir_def::expr::Literal::ByteString(b) => {
+                let mut data = vec![];
+                data.extend(0usize.to_le_bytes());
+                data.extend(b.len().to_le_bytes());
+                let mut mm = MemoryMap::default();
+                mm.insert(0, b.to_vec());
+                return Ok(Operand::from_concrete_const(data, mm, ty));
+            }
+            hir_def::expr::Literal::Char(c) => u32::from(*c).to_le_bytes().into(),
+            hir_def::expr::Literal::Bool(b) => vec![*b as u8],
+            hir_def::expr::Literal::Int(x, _) => x.to_le_bytes()[0..size].into(),
+            hir_def::expr::Literal::Uint(x, _) => x.to_le_bytes()[0..size].into(),
+            hir_def::expr::Literal::Float(f, _) => match size {
+                8 => f.into_f64().to_le_bytes().into(),
+                4 => f.into_f32().to_le_bytes().into(),
+                _ => {
+                    return Err(MirLowerError::TypeError("float with size other than 4 or 8 bytes"))
+                }
+            },
+        };
+        Ok(Operand::from_concrete_const(bytes, MemoryMap::default(), ty))
+    }
+
+    fn new_basic_block(&mut self) -> BasicBlockId {
+        self.result.basic_blocks.alloc(BasicBlock::default())
+    }
+
+    fn lower_const(
+        &mut self,
+        const_id: hir_def::ConstId,
+        prev_block: BasicBlockId,
+        place: Place,
+        span: MirSpan,
+    ) -> Result<()> {
+        let c = self.db.const_eval(const_id)?;
+        self.write_const_to_place(c, prev_block, place, span)
+    }
+
+    fn write_const_to_place(
+        &mut self,
+        c: Const,
+        prev_block: BasicBlockId,
+        place: Place,
+        span: MirSpan,
+    ) -> Result<()> {
+        self.push_assignment(prev_block, place, Operand::Constant(c).into(), span);
+        Ok(())
+    }
+
+    fn write_bytes_to_place(
+        &mut self,
+        prev_block: BasicBlockId,
+        place: Place,
+        cv: Vec<u8>,
+        ty: Ty,
+        span: MirSpan,
+    ) -> Result<()> {
+        self.push_assignment(prev_block, place, Operand::from_bytes(cv, ty).into(), span);
+        Ok(())
+    }
+
+    fn lower_enum_variant(
+        &mut self,
+        variant_id: EnumVariantId,
+        prev_block: BasicBlockId,
+        place: Place,
+        ty: Ty,
+        fields: Vec<Operand>,
+        span: MirSpan,
+    ) -> Result<BasicBlockId> {
+        let subst = match ty.kind(Interner) {
+            TyKind::Adt(_, subst) => subst.clone(),
+            _ => not_supported!("Non ADT enum"),
+        };
+        self.push_assignment(
+            prev_block,
+            place,
+            Rvalue::Aggregate(AggregateKind::Adt(variant_id.into(), subst), fields),
+            span,
+        );
+        Ok(prev_block)
+    }
+
+    fn lower_call_and_args(
+        &mut self,
+        func: Operand,
+        args: impl Iterator<Item = ExprId>,
+        place: Place,
+        mut current: BasicBlockId,
+        is_uninhabited: bool,
+    ) -> Result<Option<BasicBlockId>> {
+        let Some(args) = args
+            .map(|arg| {
+                if let Some((temp, c)) = self.lower_expr_to_some_operand(arg, current)? {
+                    current = c;
+                    Ok(Some(temp))
+                } else {
+                    Ok(None)
+                }
+            })
+            .collect::<Result<Option<Vec<_>>>>()?
+        else {
+            return Ok(None);
+        };
+        self.lower_call(func, args, place, current, is_uninhabited)
+    }
+
+    fn lower_call(
+        &mut self,
+        func: Operand,
+        args: Vec<Operand>,
+        place: Place,
+        current: BasicBlockId,
+        is_uninhabited: bool,
+    ) -> Result<Option<BasicBlockId>> {
+        let b = if is_uninhabited { None } else { Some(self.new_basic_block()) };
+        self.set_terminator(
+            current,
+            Terminator::Call {
+                func,
+                args,
+                destination: place,
+                target: b,
+                cleanup: None,
+                from_hir_call: true,
+            },
+        );
+        Ok(b)
+    }
+
+    fn is_unterminated(&mut self, source: BasicBlockId) -> bool {
+        self.result.basic_blocks[source].terminator.is_none()
+    }
+
+    fn set_terminator(&mut self, source: BasicBlockId, terminator: Terminator) {
+        self.result.basic_blocks[source].terminator = Some(terminator);
+    }
+
+    fn set_goto(&mut self, source: BasicBlockId, target: BasicBlockId) {
+        self.set_terminator(source, Terminator::Goto { target });
+    }
+
+    fn expr_ty(&self, e: ExprId) -> Ty {
+        self.infer[e].clone()
+    }
+
+    fn expr_ty_after_adjustments(&self, e: ExprId) -> Ty {
+        let mut ty = None;
+        if let Some(x) = self.infer.expr_adjustments.get(&e) {
+            if let Some(x) = x.last() {
+                ty = Some(x.target.clone());
+            }
+        }
+        ty.unwrap_or_else(|| self.expr_ty(e))
+    }
+
+    fn push_statement(&mut self, block: BasicBlockId, statement: Statement) {
+        self.result.basic_blocks[block].statements.push(statement);
+    }
+
+    fn push_assignment(
+        &mut self,
+        block: BasicBlockId,
+        place: Place,
+        rvalue: Rvalue,
+        span: MirSpan,
+    ) {
+        self.push_statement(block, StatementKind::Assign(place, rvalue).with_span(span));
+    }
+
+    /// It gets a `current` unterminated block, appends some statements and possibly a terminator to it to check if
+    /// the pattern matches and write bindings, and returns two unterminated blocks, one for the matched path (which
+    /// can be the `current` block) and one for the mismatched path. If the input pattern is irrefutable, the
+    /// mismatched path block is `None`.
+    ///
+    /// By default, it will create a new block for mismatched path. If you already have one, you can provide it with
+    /// `current_else` argument to save an unneccessary jump. If `current_else` isn't `None`, the result mismatched path
+    /// wouldn't be `None` as well. Note that this function will add jumps to the beginning of the `current_else` block,
+    /// so it should be an empty block.
+    fn pattern_match(
+        &mut self,
+        mut current: BasicBlockId,
+        mut current_else: Option<BasicBlockId>,
+        mut cond_place: Place,
+        mut cond_ty: Ty,
+        pattern: PatId,
+        mut binding_mode: BindingAnnotation,
+    ) -> Result<(BasicBlockId, Option<BasicBlockId>)> {
+        Ok(match &self.body.pats[pattern] {
+            Pat::Missing => return Err(MirLowerError::IncompleteExpr),
+            Pat::Wild => (current, current_else),
+            Pat::Tuple { args, ellipsis } => {
+                pattern_matching_dereference(&mut cond_ty, &mut binding_mode, &mut cond_place);
+                let subst = match cond_ty.kind(Interner) {
+                    TyKind::Tuple(_, s) => s,
+                    _ => {
+                        return Err(MirLowerError::TypeError(
+                            "non tuple type matched with tuple pattern",
+                        ))
+                    }
+                };
+                self.pattern_match_tuple_like(
+                    current,
+                    current_else,
+                    args.iter().enumerate().map(|(i, x)| {
+                        (
+                            PlaceElem::TupleField(i),
+                            *x,
+                            subst.at(Interner, i).assert_ty_ref(Interner).clone(),
+                        )
+                    }),
+                    *ellipsis,
+                    &cond_place,
+                    binding_mode,
+                )?
+            }
+            Pat::Or(pats) => {
+                let then_target = self.new_basic_block();
+                let mut finished = false;
+                for pat in &**pats {
+                    let (next, next_else) = self.pattern_match(
+                        current,
+                        None,
+                        cond_place.clone(),
+                        cond_ty.clone(),
+                        *pat,
+                        binding_mode,
+                    )?;
+                    self.set_goto(next, then_target);
+                    match next_else {
+                        Some(t) => {
+                            current = t;
+                        }
+                        None => {
+                            finished = true;
+                            break;
+                        }
+                    }
+                }
+                if !finished {
+                    let ce = *current_else.get_or_insert_with(|| self.new_basic_block());
+                    self.set_goto(current, ce);
+                }
+                (then_target, current_else)
+            }
+            Pat::Record { .. } => not_supported!("record pattern"),
+            Pat::Range { .. } => not_supported!("range pattern"),
+            Pat::Slice { .. } => not_supported!("slice pattern"),
+            Pat::Path(_) => {
+                let Some(variant) = self.infer.variant_resolution_for_pat(pattern) else {
+                    not_supported!("unresolved variant");
+                };
+                self.pattern_matching_variant(
+                    cond_ty,
+                    binding_mode,
+                    cond_place,
+                    variant,
+                    current,
+                    pattern.into(),
+                    current_else,
+                    &[],
+                    &None,
+                )?
+            }
+            Pat::Lit(l) => {
+                let then_target = self.new_basic_block();
+                let else_target = current_else.unwrap_or_else(|| self.new_basic_block());
+                match &self.body.exprs[*l] {
+                    Expr::Literal(l) => match l {
+                        hir_def::expr::Literal::Int(x, _) => {
+                            self.set_terminator(
+                                current,
+                                Terminator::SwitchInt {
+                                    discr: Operand::Copy(cond_place),
+                                    targets: SwitchTargets::static_if(
+                                        *x as u128,
+                                        then_target,
+                                        else_target,
+                                    ),
+                                },
+                            );
+                        }
+                        hir_def::expr::Literal::Uint(x, _) => {
+                            self.set_terminator(
+                                current,
+                                Terminator::SwitchInt {
+                                    discr: Operand::Copy(cond_place),
+                                    targets: SwitchTargets::static_if(*x, then_target, else_target),
+                                },
+                            );
+                        }
+                        _ => not_supported!("non int path literal"),
+                    },
+                    _ => not_supported!("expression path literal"),
+                }
+                (then_target, Some(else_target))
+            }
+            Pat::Bind { id, subpat } => {
+                let target_place = self.result.binding_locals[*id];
+                let mode = self.body.bindings[*id].mode;
+                if let Some(subpat) = subpat {
+                    (current, current_else) = self.pattern_match(
+                        current,
+                        current_else,
+                        cond_place.clone(),
+                        cond_ty,
+                        *subpat,
+                        binding_mode,
+                    )?
+                }
+                if matches!(mode, BindingAnnotation::Ref | BindingAnnotation::RefMut) {
+                    binding_mode = mode;
+                }
+                self.push_storage_live(*id, current)?;
+                self.push_assignment(
+                    current,
+                    target_place.into(),
+                    match binding_mode {
+                        BindingAnnotation::Unannotated | BindingAnnotation::Mutable => {
+                            Operand::Copy(cond_place).into()
+                        }
+                        BindingAnnotation::Ref => Rvalue::Ref(BorrowKind::Shared, cond_place),
+                        BindingAnnotation::RefMut => Rvalue::Ref(
+                            BorrowKind::Mut { allow_two_phase_borrow: false },
+                            cond_place,
+                        ),
+                    },
+                    pattern.into(),
+                );
+                (current, current_else)
+            }
+            Pat::TupleStruct { path: _, args, ellipsis } => {
+                let Some(variant) = self.infer.variant_resolution_for_pat(pattern) else {
+                    not_supported!("unresolved variant");
+                };
+                self.pattern_matching_variant(
+                    cond_ty,
+                    binding_mode,
+                    cond_place,
+                    variant,
+                    current,
+                    pattern.into(),
+                    current_else,
+                    args,
+                    ellipsis,
+                )?
+            }
+            Pat::Ref { .. } => not_supported!("& pattern"),
+            Pat::Box { .. } => not_supported!("box pattern"),
+            Pat::ConstBlock(_) => not_supported!("const block pattern"),
+        })
+    }
+
+    fn pattern_matching_variant(
+        &mut self,
+        mut cond_ty: Ty,
+        mut binding_mode: BindingAnnotation,
+        mut cond_place: Place,
+        variant: VariantId,
+        current: BasicBlockId,
+        span: MirSpan,
+        current_else: Option<BasicBlockId>,
+        args: &[PatId],
+        ellipsis: &Option<usize>,
+    ) -> Result<(BasicBlockId, Option<BasicBlockId>)> {
+        pattern_matching_dereference(&mut cond_ty, &mut binding_mode, &mut cond_place);
+        let subst = match cond_ty.kind(Interner) {
+            TyKind::Adt(_, s) => s,
+            _ => return Err(MirLowerError::TypeError("non adt type matched with tuple struct")),
+        };
+        let fields_type = self.db.field_types(variant);
+        Ok(match variant {
+            VariantId::EnumVariantId(v) => {
+                let e = self.db.const_eval_discriminant(v)? as u128;
+                let next = self.new_basic_block();
+                let tmp = self.discr_temp_place();
+                self.push_assignment(
+                    current,
+                    tmp.clone(),
+                    Rvalue::Discriminant(cond_place.clone()),
+                    span,
+                );
+                let else_target = current_else.unwrap_or_else(|| self.new_basic_block());
+                self.set_terminator(
+                    current,
+                    Terminator::SwitchInt {
+                        discr: Operand::Copy(tmp),
+                        targets: SwitchTargets::static_if(e, next, else_target),
+                    },
+                );
+                let enum_data = self.db.enum_data(v.parent);
+                let fields =
+                    enum_data.variants[v.local_id].variant_data.fields().iter().map(|(x, _)| {
+                        (
+                            PlaceElem::Field(FieldId { parent: v.into(), local_id: x }),
+                            fields_type[x].clone().substitute(Interner, subst),
+                        )
+                    });
+                self.pattern_match_tuple_like(
+                    next,
+                    Some(else_target),
+                    args.iter().zip(fields).map(|(x, y)| (y.0, *x, y.1)),
+                    *ellipsis,
+                    &cond_place,
+                    binding_mode,
+                )?
+            }
+            VariantId::StructId(s) => {
+                let struct_data = self.db.struct_data(s);
+                let fields = struct_data.variant_data.fields().iter().map(|(x, _)| {
+                    (
+                        PlaceElem::Field(FieldId { parent: s.into(), local_id: x }),
+                        fields_type[x].clone().substitute(Interner, subst),
+                    )
+                });
+                self.pattern_match_tuple_like(
+                    current,
+                    current_else,
+                    args.iter().zip(fields).map(|(x, y)| (y.0, *x, y.1)),
+                    *ellipsis,
+                    &cond_place,
+                    binding_mode,
+                )?
+            }
+            VariantId::UnionId(_) => {
+                return Err(MirLowerError::TypeError("pattern matching on union"))
+            }
+        })
+    }
+
+    fn pattern_match_tuple_like(
+        &mut self,
+        mut current: BasicBlockId,
+        mut current_else: Option<BasicBlockId>,
+        args: impl Iterator<Item = (PlaceElem, PatId, Ty)>,
+        ellipsis: Option<usize>,
+        cond_place: &Place,
+        binding_mode: BindingAnnotation,
+    ) -> Result<(BasicBlockId, Option<BasicBlockId>)> {
+        if ellipsis.is_some() {
+            not_supported!("tuple like pattern with ellipsis");
+        }
+        for (proj, arg, ty) in args {
+            let mut cond_place = cond_place.clone();
+            cond_place.projection.push(proj);
+            (current, current_else) =
+                self.pattern_match(current, current_else, cond_place, ty, arg, binding_mode)?;
+        }
+        Ok((current, current_else))
+    }
+
+    fn discr_temp_place(&mut self) -> Place {
+        match &self.discr_temp {
+            Some(x) => x.clone(),
+            None => {
+                let tmp: Place =
+                    self.temp(TyBuilder::discr_ty()).expect("discr_ty is never unsized").into();
+                self.discr_temp = Some(tmp.clone());
+                tmp
+            }
+        }
+    }
+
+    fn lower_loop(
+        &mut self,
+        prev_block: BasicBlockId,
+        label: Option<LabelId>,
+        f: impl FnOnce(&mut MirLowerCtx<'_>, BasicBlockId) -> Result<()>,
+    ) -> Result<Option<BasicBlockId>> {
+        if label.is_some() {
+            not_supported!("loop with label");
+        }
+        let begin = self.new_basic_block();
+        let prev =
+            mem::replace(&mut self.current_loop_blocks, Some(LoopBlocks { begin, end: None }));
+        self.set_goto(prev_block, begin);
+        f(self, begin)?;
+        let my = mem::replace(&mut self.current_loop_blocks, prev)
+            .ok_or(MirLowerError::ImplementationError("current_loop_blocks is corrupt"))?;
+        Ok(my.end)
+    }
+
+    fn has_adjustments(&self, expr_id: ExprId) -> bool {
+        !self.infer.expr_adjustments.get(&expr_id).map(|x| x.is_empty()).unwrap_or(true)
+    }
+
+    fn merge_blocks(
+        &mut self,
+        b1: Option<BasicBlockId>,
+        b2: Option<BasicBlockId>,
+    ) -> Option<BasicBlockId> {
+        match (b1, b2) {
+            (None, None) => None,
+            (None, Some(b)) | (Some(b), None) => Some(b),
+            (Some(b1), Some(b2)) => {
+                let bm = self.new_basic_block();
+                self.set_goto(b1, bm);
+                self.set_goto(b2, bm);
+                Some(bm)
+            }
+        }
+    }
+
+    fn current_loop_end(&mut self) -> Result<BasicBlockId> {
+        let r = match self
+            .current_loop_blocks
+            .as_mut()
+            .ok_or(MirLowerError::ImplementationError("Current loop access out of loop"))?
+            .end
+        {
+            Some(x) => x,
+            None => {
+                let s = self.new_basic_block();
+                self.current_loop_blocks
+                    .as_mut()
+                    .ok_or(MirLowerError::ImplementationError("Current loop access out of loop"))?
+                    .end = Some(s);
+                s
+            }
+        };
+        Ok(r)
+    }
+
+    fn is_uninhabited(&self, expr_id: ExprId) -> bool {
+        is_ty_uninhabited_from(&self.infer[expr_id], self.owner.module(self.db.upcast()), self.db)
+    }
+
+    /// This function push `StorageLive` statements for each binding in the pattern.
+    fn push_storage_live(&mut self, b: BindingId, current: BasicBlockId) -> Result<()> {
+        // Current implementation is wrong. It adds no `StorageDead` at the end of scope, and before each break
+        // and continue. It just add a `StorageDead` before the `StorageLive`, which is not wrong, but unneeeded in
+        // the proper implementation. Due this limitation, implementing a borrow checker on top of this mir will falsely
+        // allow this:
+        //
+        // ```
+        // let x;
+        // loop {
+        //     let y = 2;
+        //     x = &y;
+        //     if some_condition {
+        //         break; // we need to add a StorageDead(y) above this to kill the x borrow
+        //     }
+        // }
+        // use(x)
+        // ```
+        // But I think this approach work for mutability analysis, as user can't write code which mutates a binding
+        // after StorageDead, except loops, which are handled by this hack.
+        let span = self.body.bindings[b]
+            .definitions
+            .first()
+            .copied()
+            .map(MirSpan::PatId)
+            .unwrap_or(MirSpan::Unknown);
+        let l = self.result.binding_locals[b];
+        self.push_statement(current, StatementKind::StorageDead(l).with_span(span));
+        self.push_statement(current, StatementKind::StorageLive(l).with_span(span));
+        Ok(())
+    }
+
+    fn resolve_lang_item(&self, item: LangItem) -> Result<LangItemTarget> {
+        let crate_id = self.owner.module(self.db.upcast()).krate();
+        self.db.lang_item(crate_id, item).ok_or(MirLowerError::LangItemNotFound(item))
+    }
+
+    fn lower_block_to_place(
+        &mut self,
+        label: Option<LabelId>,
+        statements: &[hir_def::expr::Statement],
+        mut current: BasicBlockId,
+        tail: Option<ExprId>,
+        place: Place,
+    ) -> Result<Option<Idx<BasicBlock>>> {
+        if label.is_some() {
+            not_supported!("block with label");
+        }
+        for statement in statements.iter() {
+            match statement {
+                hir_def::expr::Statement::Let { pat, initializer, else_branch, type_ref: _ } => {
+                    if let Some(expr_id) = initializer {
+                        let else_block;
+                        let Some((init_place, c)) =
+                        self.lower_expr_as_place(current, *expr_id, true)?
+                    else {
+                        return Ok(None);
+                    };
+                        current = c;
+                        (current, else_block) = self.pattern_match(
+                            current,
+                            None,
+                            init_place,
+                            self.expr_ty_after_adjustments(*expr_id),
+                            *pat,
+                            BindingAnnotation::Unannotated,
+                        )?;
+                        match (else_block, else_branch) {
+                            (None, _) => (),
+                            (Some(else_block), None) => {
+                                self.set_terminator(else_block, Terminator::Unreachable);
+                            }
+                            (Some(else_block), Some(else_branch)) => {
+                                if let Some((_, b)) =
+                                    self.lower_expr_as_place(else_block, *else_branch, true)?
+                                {
+                                    self.set_terminator(b, Terminator::Unreachable);
+                                }
+                            }
+                        }
+                    }
+                }
+                hir_def::expr::Statement::Expr { expr, has_semi: _ } => {
+                    let Some((_, c)) = self.lower_expr_as_place(current, *expr, true)? else {
+                        return Ok(None);
+                    };
+                    current = c;
+                }
+            }
+        }
+        match tail {
+            Some(tail) => self.lower_expr_to_place(tail, place, current),
+            None => Ok(Some(current)),
+        }
+    }
+}
+
+fn pattern_matching_dereference(
+    cond_ty: &mut Ty,
+    binding_mode: &mut BindingAnnotation,
+    cond_place: &mut Place,
+) {
+    while let Some((ty, _, mu)) = cond_ty.as_reference() {
+        if mu == Mutability::Mut && *binding_mode != BindingAnnotation::Ref {
+            *binding_mode = BindingAnnotation::RefMut;
+        } else {
+            *binding_mode = BindingAnnotation::Ref;
+        }
+        *cond_ty = ty.clone();
+        cond_place.projection.push(ProjectionElem::Deref);
+    }
+}
+
+fn cast_kind(source_ty: &Ty, target_ty: &Ty) -> Result<CastKind> {
+    Ok(match (source_ty.kind(Interner), target_ty.kind(Interner)) {
+        (TyKind::Scalar(s), TyKind::Scalar(t)) => match (s, t) {
+            (chalk_ir::Scalar::Float(_), chalk_ir::Scalar::Float(_)) => CastKind::FloatToFloat,
+            (chalk_ir::Scalar::Float(_), _) => CastKind::FloatToInt,
+            (_, chalk_ir::Scalar::Float(_)) => CastKind::IntToFloat,
+            (_, _) => CastKind::IntToInt,
+        },
+        // Enum to int casts
+        (TyKind::Scalar(_), TyKind::Adt(..)) | (TyKind::Adt(..), TyKind::Scalar(_)) => {
+            CastKind::IntToInt
+        }
+        (a, b) => not_supported!("Unknown cast between {a:?} and {b:?}"),
+    })
+}
+
+pub fn mir_body_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Result<Arc<MirBody>> {
+    let _p = profile::span("mir_body_query").detail(|| match def {
+        DefWithBodyId::FunctionId(it) => db.function_data(it).name.to_string(),
+        DefWithBodyId::StaticId(it) => db.static_data(it).name.clone().to_string(),
+        DefWithBodyId::ConstId(it) => {
+            db.const_data(it).name.clone().unwrap_or_else(Name::missing).to_string()
+        }
+        DefWithBodyId::VariantId(it) => {
+            db.enum_data(it.parent).variants[it.local_id].name.to_string()
+        }
+    });
+    let body = db.body(def);
+    let infer = db.infer(def);
+    let result = lower_to_mir(db, def, &body, &infer, body.body_expr)?;
+    Ok(Arc::new(result))
+}
+
+pub fn mir_body_recover(
+    _db: &dyn HirDatabase,
+    _cycle: &[String],
+    _def: &DefWithBodyId,
+) -> Result<Arc<MirBody>> {
+    Err(MirLowerError::Loop)
+}
+
+pub fn lower_to_mir(
+    db: &dyn HirDatabase,
+    owner: DefWithBodyId,
+    body: &Body,
+    infer: &InferenceResult,
+    // FIXME: root_expr should always be the body.body_expr, but since `X` in `[(); X]` doesn't have its own specific body yet, we
+    // need to take this input explicitly.
+    root_expr: ExprId,
+) -> Result<MirBody> {
+    if let Some((_, x)) = infer.type_mismatches().next() {
+        return Err(MirLowerError::TypeMismatch(x.clone()));
+    }
+    let mut basic_blocks = Arena::new();
+    let start_block =
+        basic_blocks.alloc(BasicBlock { statements: vec![], terminator: None, is_cleanup: false });
+    let mut locals = Arena::new();
+    // 0 is return local
+    locals.alloc(Local { ty: infer[root_expr].clone() });
+    let mut binding_locals: ArenaMap<BindingId, LocalId> = ArenaMap::new();
+    // 1 to param_len is for params
+    let param_locals: Vec<LocalId> = if let DefWithBodyId::FunctionId(fid) = owner {
+        let substs = TyBuilder::placeholder_subst(db, fid);
+        let callable_sig = db.callable_item_signature(fid.into()).substitute(Interner, &substs);
+        body.params
+            .iter()
+            .zip(callable_sig.params().iter())
+            .map(|(&x, ty)| {
+                let local_id = locals.alloc(Local { ty: ty.clone() });
+                if let Pat::Bind { id, subpat: None } = body[x] {
+                    if matches!(
+                        body.bindings[id].mode,
+                        BindingAnnotation::Unannotated | BindingAnnotation::Mutable
+                    ) {
+                        binding_locals.insert(id, local_id);
+                    }
+                }
+                local_id
+            })
+            .collect()
+    } else {
+        if !body.params.is_empty() {
+            return Err(MirLowerError::TypeError("Unexpected parameter for non function body"));
+        }
+        vec![]
+    };
+    // and then rest of bindings
+    for (id, _) in body.bindings.iter() {
+        if !binding_locals.contains_idx(id) {
+            binding_locals.insert(id, locals.alloc(Local { ty: infer[id].clone() }));
+        }
+    }
+    let mir = MirBody {
+        basic_blocks,
+        locals,
+        start_block,
+        binding_locals,
+        param_locals,
+        owner,
+        arg_count: body.params.len(),
+    };
+    let mut ctx = MirLowerCtx {
+        result: mir,
+        db,
+        infer,
+        body,
+        owner,
+        current_loop_blocks: None,
+        discr_temp: None,
+    };
+    let mut current = start_block;
+    for (&param, local) in body.params.iter().zip(ctx.result.param_locals.clone().into_iter()) {
+        if let Pat::Bind { id, .. } = body[param] {
+            if local == ctx.result.binding_locals[id] {
+                continue;
+            }
+        }
+        let r = ctx.pattern_match(
+            current,
+            None,
+            local.into(),
+            ctx.result.locals[local].ty.clone(),
+            param,
+            BindingAnnotation::Unannotated,
+        )?;
+        if let Some(b) = r.1 {
+            ctx.set_terminator(b, Terminator::Unreachable);
+        }
+        current = r.0;
+    }
+    if let Some(b) = ctx.lower_expr_to_place(root_expr, return_slot().into(), current)? {
+        ctx.result.basic_blocks[b].terminator = Some(Terminator::Return);
+    }
+    Ok(ctx.result)
+}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs
new file mode 100644
index 00000000000..fe8147dcd3e
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs
@@ -0,0 +1,237 @@
+//! MIR lowering for places
+
+use super::*;
+use hir_expand::name;
+
+macro_rules! not_supported {
+    ($x: expr) => {
+        return Err(MirLowerError::NotSupported(format!($x)))
+    };
+}
+
+impl MirLowerCtx<'_> {
+    fn lower_expr_to_some_place_without_adjust(
+        &mut self,
+        expr_id: ExprId,
+        prev_block: BasicBlockId,
+    ) -> Result<Option<(Place, BasicBlockId)>> {
+        let ty = self.expr_ty(expr_id);
+        let place = self.temp(ty)?;
+        let Some(current) = self.lower_expr_to_place_without_adjust(expr_id, place.into(), prev_block)? else {
+            return Ok(None);
+        };
+        Ok(Some((place.into(), current)))
+    }
+
+    fn lower_expr_to_some_place_with_adjust(
+        &mut self,
+        expr_id: ExprId,
+        prev_block: BasicBlockId,
+        adjustments: &[Adjustment],
+    ) -> Result<Option<(Place, BasicBlockId)>> {
+        let ty =
+            adjustments.last().map(|x| x.target.clone()).unwrap_or_else(|| self.expr_ty(expr_id));
+        let place = self.temp(ty)?;
+        let Some(current) = self.lower_expr_to_place_with_adjust(expr_id, place.into(), prev_block, adjustments)? else {
+            return Ok(None);
+        };
+        Ok(Some((place.into(), current)))
+    }
+
+    pub(super) fn lower_expr_as_place_with_adjust(
+        &mut self,
+        current: BasicBlockId,
+        expr_id: ExprId,
+        upgrade_rvalue: bool,
+        adjustments: &[Adjustment],
+    ) -> Result<Option<(Place, BasicBlockId)>> {
+        let try_rvalue = |this: &mut MirLowerCtx<'_>| {
+            if !upgrade_rvalue {
+                return Err(MirLowerError::MutatingRvalue);
+            }
+            this.lower_expr_to_some_place_with_adjust(expr_id, current, adjustments)
+        };
+        if let Some((last, rest)) = adjustments.split_last() {
+            match last.kind {
+                Adjust::Deref(None) => {
+                    let Some(mut x) = self.lower_expr_as_place_with_adjust(
+                        current,
+                        expr_id,
+                        upgrade_rvalue,
+                        rest,
+                    )? else {
+                        return Ok(None);
+                    };
+                    x.0.projection.push(ProjectionElem::Deref);
+                    Ok(Some(x))
+                }
+                Adjust::Deref(Some(od)) => {
+                    let Some((r, current)) = self.lower_expr_as_place_with_adjust(
+                        current,
+                        expr_id,
+                        upgrade_rvalue,
+                        rest,
+                    )? else {
+                        return Ok(None);
+                    };
+                    self.lower_overloaded_deref(
+                        current,
+                        r,
+                        rest.last()
+                            .map(|x| x.target.clone())
+                            .unwrap_or_else(|| self.expr_ty(expr_id)),
+                        last.target.clone(),
+                        expr_id.into(),
+                        match od.0 {
+                            Some(Mutability::Mut) => true,
+                            Some(Mutability::Not) => false,
+                            None => {
+                                not_supported!("implicit overloaded deref with unknown mutability")
+                            }
+                        },
+                    )
+                }
+                Adjust::NeverToAny | Adjust::Borrow(_) | Adjust::Pointer(_) => try_rvalue(self),
+            }
+        } else {
+            self.lower_expr_as_place_without_adjust(current, expr_id, upgrade_rvalue)
+        }
+    }
+
+    pub(super) fn lower_expr_as_place(
+        &mut self,
+        current: BasicBlockId,
+        expr_id: ExprId,
+        upgrade_rvalue: bool,
+    ) -> Result<Option<(Place, BasicBlockId)>> {
+        match self.infer.expr_adjustments.get(&expr_id) {
+            Some(a) => self.lower_expr_as_place_with_adjust(current, expr_id, upgrade_rvalue, a),
+            None => self.lower_expr_as_place_without_adjust(current, expr_id, upgrade_rvalue),
+        }
+    }
+
+    pub(super) fn lower_expr_as_place_without_adjust(
+        &mut self,
+        current: BasicBlockId,
+        expr_id: ExprId,
+        upgrade_rvalue: bool,
+    ) -> Result<Option<(Place, BasicBlockId)>> {
+        let try_rvalue = |this: &mut MirLowerCtx<'_>| {
+            if !upgrade_rvalue {
+                return Err(MirLowerError::MutatingRvalue);
+            }
+            this.lower_expr_to_some_place_without_adjust(expr_id, current)
+        };
+        match &self.body.exprs[expr_id] {
+            Expr::Path(p) => {
+                let resolver = resolver_for_expr(self.db.upcast(), self.owner, expr_id);
+                let Some(pr) = resolver.resolve_path_in_value_ns(self.db.upcast(), p.mod_path()) else {
+                    return Err(MirLowerError::unresolved_path(self.db, p));
+                };
+                let pr = match pr {
+                    ResolveValueResult::ValueNs(v) => v,
+                    ResolveValueResult::Partial(..) => return try_rvalue(self),
+                };
+                match pr {
+                    ValueNs::LocalBinding(pat_id) => {
+                        Ok(Some((self.result.binding_locals[pat_id].into(), current)))
+                    }
+                    _ => try_rvalue(self),
+                }
+            }
+            Expr::UnaryOp { expr, op } => match op {
+                hir_def::expr::UnaryOp::Deref => {
+                    if !matches!(
+                        self.expr_ty(*expr).kind(Interner),
+                        TyKind::Ref(..) | TyKind::Raw(..)
+                    ) {
+                        let Some(_) = self.lower_expr_as_place(current, *expr, true)? else {
+                            return Ok(None);
+                        };
+                        not_supported!("explicit overloaded deref");
+                    }
+                    let Some((mut r, current)) = self.lower_expr_as_place(current, *expr, true)? else {
+                        return Ok(None);
+                    };
+                    r.projection.push(ProjectionElem::Deref);
+                    Ok(Some((r, current)))
+                }
+                _ => try_rvalue(self),
+            },
+            Expr::Field { expr, .. } => {
+                let Some((mut r, current)) = self.lower_expr_as_place(current, *expr, true)? else {
+                    return Ok(None);
+                };
+                self.push_field_projection(&mut r, expr_id)?;
+                Ok(Some((r, current)))
+            }
+            Expr::Index { base, index } => {
+                let base_ty = self.expr_ty_after_adjustments(*base);
+                let index_ty = self.expr_ty_after_adjustments(*index);
+                if index_ty != TyBuilder::usize()
+                    || !matches!(base_ty.kind(Interner), TyKind::Array(..) | TyKind::Slice(..))
+                {
+                    not_supported!("overloaded index");
+                }
+                let Some((mut p_base, current)) =
+                    self.lower_expr_as_place(current, *base, true)? else {
+                    return Ok(None);
+                };
+                let l_index = self.temp(self.expr_ty_after_adjustments(*index))?;
+                let Some(current) = self.lower_expr_to_place(*index, l_index.into(), current)? else {
+                    return Ok(None);
+                };
+                p_base.projection.push(ProjectionElem::Index(l_index));
+                Ok(Some((p_base, current)))
+            }
+            _ => try_rvalue(self),
+        }
+    }
+
+    fn lower_overloaded_deref(
+        &mut self,
+        current: BasicBlockId,
+        place: Place,
+        source_ty: Ty,
+        target_ty: Ty,
+        span: MirSpan,
+        mutability: bool,
+    ) -> Result<Option<(Place, BasicBlockId)>> {
+        let (chalk_mut, trait_lang_item, trait_method_name, borrow_kind) = if !mutability {
+            (Mutability::Not, LangItem::Deref, name![deref], BorrowKind::Shared)
+        } else {
+            (
+                Mutability::Mut,
+                LangItem::DerefMut,
+                name![deref_mut],
+                BorrowKind::Mut { allow_two_phase_borrow: false },
+            )
+        };
+        let ty_ref = TyKind::Ref(chalk_mut, static_lifetime(), source_ty.clone()).intern(Interner);
+        let target_ty_ref = TyKind::Ref(chalk_mut, static_lifetime(), target_ty).intern(Interner);
+        let ref_place: Place = self.temp(ty_ref)?.into();
+        self.push_assignment(current, ref_place.clone(), Rvalue::Ref(borrow_kind, place), span);
+        let deref_trait = self
+            .resolve_lang_item(trait_lang_item)?
+            .as_trait()
+            .ok_or(MirLowerError::LangItemNotFound(trait_lang_item))?;
+        let deref_fn = self
+            .db
+            .trait_data(deref_trait)
+            .method_by_name(&trait_method_name)
+            .ok_or(MirLowerError::LangItemNotFound(trait_lang_item))?;
+        let deref_fn_op = Operand::const_zst(
+            TyKind::FnDef(
+                self.db.intern_callable_def(CallableDefId::FunctionId(deref_fn)).into(),
+                Substitution::from1(Interner, source_ty),
+            )
+            .intern(Interner),
+        );
+        let mut result: Place = self.temp(target_ty_ref)?.into();
+        let Some(current) = self.lower_call(deref_fn_op, vec![Operand::Copy(ref_place)], result.clone(), current, false)? else {
+            return Ok(None);
+        };
+        result.projection.push(ProjectionElem::Deref);
+        Ok(Some((result, current)))
+    }
+}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs
new file mode 100644
index 00000000000..ffc08b7e346
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs
@@ -0,0 +1,348 @@
+//! A pretty-printer for MIR.
+
+use std::fmt::{Display, Write};
+
+use hir_def::{body::Body, expr::BindingId};
+use hir_expand::name::Name;
+use la_arena::ArenaMap;
+
+use crate::{
+    db::HirDatabase,
+    display::HirDisplay,
+    mir::{PlaceElem, ProjectionElem, StatementKind, Terminator},
+};
+
+use super::{
+    AggregateKind, BasicBlockId, BorrowKind, LocalId, MirBody, Operand, Place, Rvalue, UnOp,
+};
+
+impl MirBody {
+    pub fn pretty_print(&self, db: &dyn HirDatabase) -> String {
+        let hir_body = db.body(self.owner);
+        let mut ctx = MirPrettyCtx::new(self, &hir_body, db);
+        ctx.for_body();
+        ctx.result
+    }
+}
+
+struct MirPrettyCtx<'a> {
+    body: &'a MirBody,
+    hir_body: &'a Body,
+    db: &'a dyn HirDatabase,
+    result: String,
+    ident: String,
+    local_to_binding: ArenaMap<LocalId, BindingId>,
+}
+
+macro_rules! w {
+    ($dst:expr, $($arg:tt)*) => {
+        { let _ = write!($dst, $($arg)*); }
+    };
+}
+
+macro_rules! wln {
+    ($dst:expr) => {
+        { let _ = writeln!($dst); }
+    };
+    ($dst:expr, $($arg:tt)*) => {
+        { let _ = writeln!($dst, $($arg)*); }
+    };
+}
+
+impl Write for MirPrettyCtx<'_> {
+    fn write_str(&mut self, s: &str) -> std::fmt::Result {
+        let mut it = s.split('\n'); // note: `.lines()` is wrong here
+        self.write(it.next().unwrap_or_default());
+        for line in it {
+            self.write_line();
+            self.write(line);
+        }
+        Ok(())
+    }
+}
+
+enum LocalName {
+    Unknown(LocalId),
+    Binding(Name, LocalId),
+}
+
+impl Display for LocalName {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            LocalName::Unknown(l) => write!(f, "_{}", u32::from(l.into_raw())),
+            LocalName::Binding(n, l) => write!(f, "{n}_{}", u32::from(l.into_raw())),
+        }
+    }
+}
+
+impl<'a> MirPrettyCtx<'a> {
+    fn for_body(&mut self) {
+        self.with_block(|this| {
+            this.locals();
+            wln!(this);
+            this.blocks();
+        });
+    }
+
+    fn with_block(&mut self, f: impl FnOnce(&mut MirPrettyCtx<'_>)) {
+        self.ident += "    ";
+        wln!(self, "{{");
+        f(self);
+        for _ in 0..4 {
+            self.result.pop();
+            self.ident.pop();
+        }
+        wln!(self, "}}");
+    }
+
+    fn new(body: &'a MirBody, hir_body: &'a Body, db: &'a dyn HirDatabase) -> Self {
+        let local_to_binding = body.binding_locals.iter().map(|(x, y)| (*y, x)).collect();
+        MirPrettyCtx {
+            body,
+            db,
+            result: String::new(),
+            ident: String::new(),
+            local_to_binding,
+            hir_body,
+        }
+    }
+
+    fn write_line(&mut self) {
+        self.result.push('\n');
+        self.result += &self.ident;
+    }
+
+    fn write(&mut self, line: &str) {
+        self.result += line;
+    }
+
+    fn locals(&mut self) {
+        for (id, local) in self.body.locals.iter() {
+            wln!(self, "let {}: {};", self.local_name(id), local.ty.display(self.db));
+        }
+    }
+
+    fn local_name(&self, local: LocalId) -> LocalName {
+        match self.local_to_binding.get(local) {
+            Some(b) => LocalName::Binding(self.hir_body.bindings[*b].name.clone(), local),
+            None => LocalName::Unknown(local),
+        }
+    }
+
+    fn basic_block_id(&self, basic_block_id: BasicBlockId) -> String {
+        format!("'bb{}", u32::from(basic_block_id.into_raw()))
+    }
+
+    fn blocks(&mut self) {
+        for (id, block) in self.body.basic_blocks.iter() {
+            wln!(self);
+            w!(self, "{}: ", self.basic_block_id(id));
+            self.with_block(|this| {
+                for statement in &block.statements {
+                    match &statement.kind {
+                        StatementKind::Assign(l, r) => {
+                            this.place(l);
+                            w!(this, " = ");
+                            this.rvalue(r);
+                            wln!(this, ";");
+                        }
+                        StatementKind::StorageDead(p) => {
+                            wln!(this, "StorageDead({})", this.local_name(*p));
+                        }
+                        StatementKind::StorageLive(p) => {
+                            wln!(this, "StorageLive({})", this.local_name(*p));
+                        }
+                        StatementKind::Deinit(p) => {
+                            w!(this, "Deinit(");
+                            this.place(p);
+                            wln!(this, ");");
+                        }
+                        StatementKind::Nop => wln!(this, "Nop;"),
+                    }
+                }
+                match &block.terminator {
+                    Some(terminator) => match terminator {
+                        Terminator::Goto { target } => {
+                            wln!(this, "goto 'bb{};", u32::from(target.into_raw()))
+                        }
+                        Terminator::SwitchInt { discr, targets } => {
+                            w!(this, "switch ");
+                            this.operand(discr);
+                            w!(this, " ");
+                            this.with_block(|this| {
+                                for (c, b) in targets.iter() {
+                                    wln!(this, "{c} => {},", this.basic_block_id(b));
+                                }
+                                wln!(this, "_ => {},", this.basic_block_id(targets.otherwise()));
+                            });
+                        }
+                        Terminator::Call { func, args, destination, target, .. } => {
+                            w!(this, "Call ");
+                            this.with_block(|this| {
+                                w!(this, "func: ");
+                                this.operand(func);
+                                wln!(this, ",");
+                                w!(this, "args: [");
+                                this.operand_list(args);
+                                wln!(this, "],");
+                                w!(this, "destination: ");
+                                this.place(destination);
+                                wln!(this, ",");
+                                w!(this, "target: ");
+                                match target {
+                                    Some(t) => w!(this, "{}", this.basic_block_id(*t)),
+                                    None => w!(this, "<unreachable>"),
+                                }
+                                wln!(this, ",");
+                            });
+                        }
+                        _ => wln!(this, "{:?};", terminator),
+                    },
+                    None => wln!(this, "<no-terminator>;"),
+                }
+            })
+        }
+    }
+
+    fn place(&mut self, p: &Place) {
+        fn f(this: &mut MirPrettyCtx<'_>, local: LocalId, projections: &[PlaceElem]) {
+            let Some((last, head)) = projections.split_last() else {
+                // no projection
+                w!(this, "{}", this.local_name(local));
+                return;
+            };
+            match last {
+                ProjectionElem::Deref => {
+                    w!(this, "(*");
+                    f(this, local, head);
+                    w!(this, ")");
+                }
+                ProjectionElem::Field(field) => {
+                    let variant_data = field.parent.variant_data(this.db.upcast());
+                    let name = &variant_data.fields()[field.local_id].name;
+                    match field.parent {
+                        hir_def::VariantId::EnumVariantId(e) => {
+                            w!(this, "(");
+                            f(this, local, head);
+                            let variant_name =
+                                &this.db.enum_data(e.parent).variants[e.local_id].name;
+                            w!(this, " as {}).{}", variant_name, name);
+                        }
+                        hir_def::VariantId::StructId(_) | hir_def::VariantId::UnionId(_) => {
+                            f(this, local, head);
+                            w!(this, ".{name}");
+                        }
+                    }
+                }
+                ProjectionElem::TupleField(x) => {
+                    f(this, local, head);
+                    w!(this, ".{}", x);
+                }
+                ProjectionElem::Index(l) => {
+                    f(this, local, head);
+                    w!(this, "[{}]", this.local_name(*l));
+                }
+                x => {
+                    f(this, local, head);
+                    w!(this, ".{:?}", x);
+                }
+            }
+        }
+        f(self, p.local, &p.projection);
+    }
+
+    fn operand(&mut self, r: &Operand) {
+        match r {
+            Operand::Copy(p) | Operand::Move(p) => {
+                // MIR at the time of writing doesn't have difference between move and copy, so we show them
+                // equally. Feel free to change it.
+                self.place(p);
+            }
+            Operand::Constant(c) => w!(self, "Const({})", c.display(self.db)),
+        }
+    }
+
+    fn rvalue(&mut self, r: &Rvalue) {
+        match r {
+            Rvalue::Use(op) => self.operand(op),
+            Rvalue::Ref(r, p) => {
+                match r {
+                    BorrowKind::Shared => w!(self, "&"),
+                    BorrowKind::Shallow => w!(self, "&shallow "),
+                    BorrowKind::Unique => w!(self, "&uniq "),
+                    BorrowKind::Mut { .. } => w!(self, "&mut "),
+                }
+                self.place(p);
+            }
+            Rvalue::Aggregate(AggregateKind::Tuple(_), x) => {
+                w!(self, "(");
+                self.operand_list(x);
+                w!(self, ")");
+            }
+            Rvalue::Aggregate(AggregateKind::Array(_), x) => {
+                w!(self, "[");
+                self.operand_list(x);
+                w!(self, "]");
+            }
+            Rvalue::Aggregate(AggregateKind::Adt(_, _), x) => {
+                w!(self, "Adt(");
+                self.operand_list(x);
+                w!(self, ")");
+            }
+            Rvalue::Aggregate(AggregateKind::Union(_, _), x) => {
+                w!(self, "Union(");
+                self.operand_list(x);
+                w!(self, ")");
+            }
+            Rvalue::Len(p) => {
+                w!(self, "Len(");
+                self.place(p);
+                w!(self, ")");
+            }
+            Rvalue::Cast(ck, op, ty) => {
+                w!(self, "Discriminant({ck:?}");
+                self.operand(op);
+                w!(self, "{})", ty.display(self.db));
+            }
+            Rvalue::CheckedBinaryOp(b, o1, o2) => {
+                self.operand(o1);
+                w!(self, " {b} ");
+                self.operand(o2);
+            }
+            Rvalue::UnaryOp(u, o) => {
+                let u = match u {
+                    UnOp::Not => "!",
+                    UnOp::Neg => "-",
+                };
+                w!(self, "{u} ");
+                self.operand(o);
+            }
+            Rvalue::Discriminant(p) => {
+                w!(self, "Discriminant(");
+                self.place(p);
+                w!(self, ")");
+            }
+            Rvalue::ShallowInitBox(op, _) => {
+                w!(self, "ShallowInitBox(");
+                self.operand(op);
+                w!(self, ")");
+            }
+            Rvalue::CopyForDeref(p) => {
+                w!(self, "CopyForDeref(");
+                self.place(p);
+                w!(self, ")");
+            }
+        }
+    }
+
+    fn operand_list(&mut self, x: &[Operand]) {
+        let mut it = x.iter();
+        if let Some(first) = it.next() {
+            self.operand(first);
+            for op in it {
+                w!(self, ", ");
+                self.operand(op);
+            }
+        }
+    }
+}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs
index ba5d9c24126..bcd63d9472a 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs
@@ -61,22 +61,27 @@ fn setup_tracing() -> Option<tracing::subscriber::DefaultGuard> {
     Some(tracing::subscriber::set_default(subscriber))
 }
 
+#[track_caller]
 fn check_types(ra_fixture: &str) {
     check_impl(ra_fixture, false, true, false)
 }
 
+#[track_caller]
 fn check_types_source_code(ra_fixture: &str) {
     check_impl(ra_fixture, false, true, true)
 }
 
+#[track_caller]
 fn check_no_mismatches(ra_fixture: &str) {
     check_impl(ra_fixture, true, false, false)
 }
 
+#[track_caller]
 fn check(ra_fixture: &str) {
     check_impl(ra_fixture, false, false, false)
 }
 
+#[track_caller]
 fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_source: bool) {
     let _tracing = setup_tracing();
     let (db, files) = TestDB::with_many_files(ra_fixture);
@@ -158,7 +163,7 @@ fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_sour
                 } else {
                     ty.display_test(&db).to_string()
                 };
-                assert_eq!(actual, expected);
+                assert_eq!(actual, expected, "type annotation differs at {:#?}", range.range);
             }
         }
 
@@ -174,7 +179,7 @@ fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_sour
                 } else {
                     ty.display_test(&db).to_string()
                 };
-                assert_eq!(actual, expected);
+                assert_eq!(actual, expected, "type annotation differs at {:#?}", range.range);
             }
             if let Some(expected) = adjustments.remove(&range) {
                 let adjustments = inference_result
@@ -191,30 +196,11 @@ fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_sour
             }
         }
 
-        for (pat, mismatch) in inference_result.pat_type_mismatches() {
-            let node = match pat_node(&body_source_map, pat, &db) {
-                Some(value) => value,
-                None => continue,
-            };
-            let range = node.as_ref().original_file_range(&db);
-            let actual = format!(
-                "expected {}, got {}",
-                mismatch.expected.display_test(&db),
-                mismatch.actual.display_test(&db)
-            );
-            match mismatches.remove(&range) {
-                Some(annotation) => assert_eq!(actual, annotation),
-                None => format_to!(unexpected_type_mismatches, "{:?}: {}\n", range.range, actual),
-            }
-        }
-        for (expr, mismatch) in inference_result.expr_type_mismatches() {
-            let node = match body_source_map.expr_syntax(expr) {
-                Ok(sp) => {
-                    let root = db.parse_or_expand(sp.file_id).unwrap();
-                    sp.map(|ptr| ptr.to_node(&root).syntax().clone())
-                }
-                Err(SyntheticSyntax) => continue,
-            };
+        for (expr_or_pat, mismatch) in inference_result.type_mismatches() {
+            let Some(node) = (match expr_or_pat {
+                hir_def::expr::ExprOrPatId::ExprId(expr) => expr_node(&body_source_map, expr, &db),
+                hir_def::expr::ExprOrPatId::PatId(pat) => pat_node(&body_source_map, pat, &db),
+            }) else { continue; };
             let range = node.as_ref().original_file_range(&db);
             let actual = format!(
                 "expected {}, got {}",
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs
index 3e110abaf4b..b524922b6cf 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs
@@ -258,6 +258,7 @@ fn test() {
 
 #[test]
 fn coerce_autoderef_block() {
+    // FIXME: We should know mutability in overloaded deref
     check_no_mismatches(
         r#"
 //- minicore: deref
@@ -267,7 +268,7 @@ fn takes_ref_str(x: &str) {}
 fn returns_string() -> String { loop {} }
 fn test() {
     takes_ref_str(&{ returns_string() });
-               // ^^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(None), Deref(Some(OverloadedDeref(Not))), Borrow(Ref(Not))
+               // ^^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(None), Deref(Some(OverloadedDeref(None))), Borrow(Ref(Not))
 }
 "#,
     );
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/diagnostics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/diagnostics.rs
index f00fa972948..1876be303ad 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/diagnostics.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/diagnostics.rs
@@ -73,3 +73,24 @@ fn test(x: bool) -> &'static str {
 "#,
     );
 }
+
+#[test]
+fn non_unit_block_expr_stmt_no_semi() {
+    check(
+        r#"
+fn test(x: bool) {
+    if x {
+        "notok"
+      //^^^^^^^ expected (), got &str
+    } else {
+        "ok"
+      //^^^^ expected (), got &str
+    }
+    match x { true => true, false => 0 }
+  //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected (), got bool
+                                   //^ expected bool, got i32
+    ()
+}
+"#,
+    );
+}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs
index 41c53701df6..e568e7013fa 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs
@@ -1167,7 +1167,6 @@ fn test() {
             123..167 '{     ...o(); }': ()
             133..134 's': &S
             137..151 'unsafe { f() }': &S
-            137..151 'unsafe { f() }': &S
             146..147 'f': fn f() -> &S
             146..149 'f()': &S
             157..158 's': &S
@@ -1253,6 +1252,7 @@ fn foo<T: Trait>(a: &T) {
 
 #[test]
 fn autoderef_visibility_field() {
+    // FIXME: We should know mutability in overloaded deref
     check(
         r#"
 //- minicore: deref
@@ -1274,7 +1274,7 @@ mod a {
 mod b {
     fn foo() {
         let x = super::a::Bar::new().0;
-             // ^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(Some(OverloadedDeref(Not)))
+             // ^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(Some(OverloadedDeref(None)))
              // ^^^^^^^^^^^^^^^^^^^^^^ type: char
     }
 }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs
index 9333e269352..74bcab6caa9 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs
@@ -476,7 +476,7 @@ fn infer_adt_pattern() {
             183..184 'x': usize
             190..191 'x': usize
             201..205 'E::B': E
-            209..212 'foo': bool
+            209..212 'foo': {unknown}
             216..217 '1': usize
             227..231 'E::B': E
             235..237 '10': usize
@@ -953,9 +953,9 @@ fn main() {
             42..51 'true | ()': bool
             49..51 '()': ()
             57..59 '{}': ()
-            68..80 '(() | true,)': ((),)
+            68..80 '(() | true,)': (bool,)
             69..71 '()': ()
-            69..78 '() | true': ()
+            69..78 '() | true': bool
             74..78 'true': bool
             74..78 'true': bool
             84..86 '{}': ()
@@ -964,19 +964,15 @@ fn main() {
             96..102 '_ | ()': bool
             100..102 '()': ()
             108..110 '{}': ()
-            119..128 '(() | _,)': ((),)
+            119..128 '(() | _,)': (bool,)
             120..122 '()': ()
-            120..126 '() | _': ()
+            120..126 '() | _': bool
             125..126 '_': bool
             132..134 '{}': ()
             49..51: expected bool, got ()
-            68..80: expected (bool,), got ((),)
             69..71: expected bool, got ()
-            69..78: expected bool, got ()
             100..102: expected bool, got ()
-            119..128: expected (bool,), got ((),)
             120..122: expected bool, got ()
-            120..126: expected bool, got ()
         "#]],
     );
 }
@@ -1092,3 +1088,19 @@ fn my_fn(foo: ...) {}
 "#,
     );
 }
+
+#[test]
+fn ref_pat_mutability() {
+    check(
+        r#"
+fn foo() {
+    let &() = &();
+    let &mut () = &mut ();
+    let &mut () = &();
+      //^^^^^^^ expected &(), got &mut ()
+    let &() = &mut ();
+      //^^^ expected &mut (), got &()
+}
+"#,
+    );
+}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs
index de6ae7fff8f..e6b4f13c8d1 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs
@@ -270,7 +270,7 @@ fn infer_std_crash_5() {
             61..320 '{     ...     }': ()
             75..79 'name': &{unknown}
             82..166 'if doe...     }': &{unknown}
-            85..98 'doesnt_matter': bool
+            85..98 'doesnt_matter': {unknown}
             99..128 '{     ...     }': &{unknown}
             113..118 'first': &{unknown}
             134..166 '{     ...     }': &{unknown}
@@ -279,7 +279,7 @@ fn infer_std_crash_5() {
             181..188 'content': &{unknown}
             191..313 'if ICE...     }': &{unknown}
             194..231 'ICE_RE..._VALUE': {unknown}
-            194..247 'ICE_RE...&name)': bool
+            194..247 'ICE_RE...&name)': {unknown}
             241..246 '&name': &&{unknown}
             242..246 'name': &{unknown}
             248..276 '{     ...     }': &{unknown}
@@ -1015,9 +1015,9 @@ fn cfg_tail() {
             20..31 '{ "first" }': ()
             22..29 '"first"': &str
             72..190 '{     ...] 13 }': ()
-            78..88 '{ "fake" }': &str
+            78..88 '{ "fake" }': ()
             80..86 '"fake"': &str
-            93..103 '{ "fake" }': &str
+            93..103 '{ "fake" }': ()
             95..101 '"fake"': &str
             108..120 '{ "second" }': ()
             110..118 '"second"': &str
@@ -1744,3 +1744,15 @@ fn foo(b: Bar) {
 "#,
     );
 }
+
+#[test]
+fn regression_14305() {
+    check_no_mismatches(
+        r#"
+//- minicore: add
+trait Tr {}
+impl Tr for [u8; C] {}
+const C: usize = 2 + 2;
+"#,
+    );
+}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs
index 2e5787b701c..0e9c349afef 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs
@@ -352,7 +352,6 @@ unsafe fn baz(u: MyUnion) {
             71..89 'MyUnio...o: 0 }': MyUnion
             86..87 '0': u32
             95..113 'unsafe...(u); }': ()
-            95..113 'unsafe...(u); }': ()
             104..107 'baz': fn baz(MyUnion)
             104..110 'baz(u)': ()
             108..109 'u': MyUnion
@@ -360,7 +359,6 @@ unsafe fn baz(u: MyUnion) {
             126..146 'MyUnio... 0.0 }': MyUnion
             141..144 '0.0': f32
             152..170 'unsafe...(u); }': ()
-            152..170 'unsafe...(u); }': ()
             161..164 'baz': fn baz(MyUnion)
             161..167 'baz(u)': ()
             165..166 'u': MyUnion
@@ -2077,22 +2075,17 @@ async fn main() {
             16..193 '{     ...2 }; }': ()
             26..27 'x': i32
             30..43 'unsafe { 92 }': i32
-            30..43 'unsafe { 92 }': i32
             39..41 '92': i32
             53..54 'y': impl Future<Output = ()>
-            57..85 'async ...wait }': ()
             57..85 'async ...wait }': impl Future<Output = ()>
-            65..77 'async { () }': ()
             65..77 'async { () }': impl Future<Output = ()>
             65..83 'async ....await': ()
             73..75 '()': ()
             95..96 'z': ControlFlow<(), ()>
-            130..140 'try { () }': ()
             130..140 'try { () }': ControlFlow<(), ()>
             136..138 '()': ()
             150..151 'w': i32
             154..166 'const { 92 }': i32
-            154..166 'const { 92 }': i32
             162..164 '92': i32
             176..177 't': i32
             180..190 ''a: { 92 }': i32
@@ -2122,7 +2115,6 @@ fn main() {
             83..84 'f': F
             89..91 '{}': ()
             103..231 '{     ... }); }': ()
-            109..161 'async ...     }': Result<(), ()>
             109..161 'async ...     }': impl Future<Output = Result<(), ()>>
             125..139 'return Err(())': !
             132..135 'Err': Err<(), ()>(()) -> Result<(), ()>
@@ -2134,7 +2126,6 @@ fn main() {
             167..171 'test': fn test<(), (), || -> impl Future<Output = Result<(), ()>>, impl Future<Output = Result<(), ()>>>(|| -> impl Future<Output = Result<(), ()>>)
             167..228 'test(|...    })': ()
             172..227 '|| asy...     }': || -> impl Future<Output = Result<(), ()>>
-            175..227 'async ...     }': Result<(), ()>
             175..227 'async ...     }': impl Future<Output = Result<(), ()>>
             191..205 'return Err(())': !
             198..201 'Err': Err<(), ()>(()) -> Result<(), ()>
@@ -3283,3 +3274,18 @@ fn func() {
         "#]],
     );
 }
+
+#[test]
+fn issue_14275() {
+    // FIXME: evaluate const generic
+    check_types(
+        r#"
+struct Foo<const T: bool>;
+fn main() {
+    const B: bool = false;
+    let foo = Foo::<B>;
+      //^^^ Foo<_>
+}
+"#,
+    );
+}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs b/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs
index 70d2d5efa6c..34d957e26ef 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs
@@ -5,6 +5,7 @@ use std::iter;
 
 use base_db::CrateId;
 use chalk_ir::{cast::Cast, fold::Shift, BoundVar, DebruijnIndex};
+use either::Either;
 use hir_def::{
     db::DefDatabase,
     generics::{
@@ -19,7 +20,6 @@ use hir_def::{
 };
 use hir_expand::name::Name;
 use intern::Interned;
-use itertools::Either;
 use rustc_hash::FxHashSet;
 use smallvec::{smallvec, SmallVec};
 
@@ -315,7 +315,10 @@ fn parent_generic_def(db: &dyn DefDatabase, def: GenericDefId) -> Option<Generic
         GenericDefId::TypeAliasId(it) => it.lookup(db).container,
         GenericDefId::ConstId(it) => it.lookup(db).container,
         GenericDefId::EnumVariantId(it) => return Some(it.parent.into()),
-        GenericDefId::AdtId(_) | GenericDefId::TraitId(_) | GenericDefId::ImplId(_) => return None,
+        GenericDefId::AdtId(_)
+        | GenericDefId::TraitId(_)
+        | GenericDefId::ImplId(_)
+        | GenericDefId::TraitAliasId(_) => return None,
     };
 
     match container {
diff --git a/src/tools/rust-analyzer/crates/hir/src/attrs.rs b/src/tools/rust-analyzer/crates/hir/src/attrs.rs
index 54425d69b6b..db0b84ef088 100644
--- a/src/tools/rust-analyzer/crates/hir/src/attrs.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/attrs.rs
@@ -14,7 +14,8 @@ use syntax::{ast, AstNode};
 
 use crate::{
     Adt, AssocItem, Const, ConstParam, Enum, Field, Function, GenericParam, Impl, LifetimeParam,
-    Macro, Module, ModuleDef, Static, Struct, Trait, TypeAlias, TypeParam, Union, Variant,
+    Macro, Module, ModuleDef, Static, Struct, Trait, TraitAlias, TypeAlias, TypeParam, Union,
+    Variant,
 };
 
 pub trait HasAttrs {
@@ -60,6 +61,7 @@ impl_has_attrs![
     (Static, StaticId),
     (Const, ConstId),
     (Trait, TraitId),
+    (TraitAlias, TraitAliasId),
     (TypeAlias, TypeAliasId),
     (Macro, MacroId),
     (Function, FunctionId),
@@ -134,6 +136,7 @@ fn resolve_doc_path(
         AttrDefId::StaticId(it) => it.resolver(db.upcast()),
         AttrDefId::ConstId(it) => it.resolver(db.upcast()),
         AttrDefId::TraitId(it) => it.resolver(db.upcast()),
+        AttrDefId::TraitAliasId(it) => it.resolver(db.upcast()),
         AttrDefId::TypeAliasId(it) => it.resolver(db.upcast()),
         AttrDefId::ImplId(it) => it.resolver(db.upcast()),
         AttrDefId::ExternBlockId(it) => it.resolver(db.upcast()),
diff --git a/src/tools/rust-analyzer/crates/hir/src/db.rs b/src/tools/rust-analyzer/crates/hir/src/db.rs
index e25d8678458..cd465739139 100644
--- a/src/tools/rust-analyzer/crates/hir/src/db.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/db.rs
@@ -5,8 +5,9 @@
 //! But we need this for at least LRU caching at the query level.
 pub use hir_def::db::*;
 pub use hir_expand::db::{
-    AstDatabase, AstDatabaseStorage, AstIdMapQuery, HygieneFrameQuery, InternMacroCallQuery,
-    MacroArgTextQuery, MacroDefQuery, MacroExpandQuery, ParseMacroExpansionQuery,
+    AstDatabase, AstDatabaseStorage, AstIdMapQuery, ExpandProcMacroQuery, HygieneFrameQuery,
+    InternMacroCallQuery, MacroArgTextQuery, MacroDefQuery, MacroExpandErrorQuery,
+    MacroExpandQuery, ParseMacroExpansionQuery,
 };
 pub use hir_ty::db::*;
 
diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs
index 54d43fa8dc7..8f019a81b2d 100644
--- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs
@@ -10,7 +10,7 @@ use hir_def::path::ModPath;
 use hir_expand::{name::Name, HirFileId, InFile};
 use syntax::{ast, AstPtr, SyntaxNodePtr, TextRange};
 
-use crate::{AssocItem, Field, MacroKind, Type};
+use crate::{AssocItem, Field, Local, MacroKind, Type};
 
 macro_rules! diagnostics {
     ($($diag:ident,)*) => {
@@ -31,6 +31,7 @@ macro_rules! diagnostics {
 
 diagnostics![
     BreakOutsideOfLoop,
+    ExpectedFunction,
     InactiveCode,
     IncorrectCase,
     InvalidDeriveTarget,
@@ -40,6 +41,7 @@ diagnostics![
     MissingFields,
     MissingMatchArms,
     MissingUnsafe,
+    NeedMut,
     NoSuchField,
     PrivateAssocItem,
     PrivateField,
@@ -47,10 +49,13 @@ diagnostics![
     TypeMismatch,
     UnimplementedBuiltinMacro,
     UnresolvedExternCrate,
+    UnresolvedField,
     UnresolvedImport,
     UnresolvedMacroCall,
+    UnresolvedMethodCall,
     UnresolvedModule,
     UnresolvedProcMacro,
+    UnusedMut,
 ];
 
 #[derive(Debug)]
@@ -131,6 +136,28 @@ pub struct PrivateAssocItem {
 }
 
 #[derive(Debug)]
+pub struct ExpectedFunction {
+    pub call: InFile<AstPtr<ast::Expr>>,
+    pub found: Type,
+}
+
+#[derive(Debug)]
+pub struct UnresolvedField {
+    pub expr: InFile<AstPtr<ast::Expr>>,
+    pub receiver: Type,
+    pub name: Name,
+    pub method_with_same_name_exists: bool,
+}
+
+#[derive(Debug)]
+pub struct UnresolvedMethodCall {
+    pub expr: InFile<AstPtr<ast::Expr>>,
+    pub receiver: Type,
+    pub name: Name,
+    pub field_with_same_name: Option<Type>,
+}
+
+#[derive(Debug)]
 pub struct PrivateField {
     pub expr: InFile<AstPtr<ast::Expr>>,
     pub field: Field,
@@ -140,6 +167,7 @@ pub struct PrivateField {
 pub struct BreakOutsideOfLoop {
     pub expr: InFile<AstPtr<ast::Expr>>,
     pub is_break: bool,
+    pub bad_value_break: bool,
 }
 
 #[derive(Debug)]
@@ -171,17 +199,26 @@ pub struct MismatchedArgCount {
 
 #[derive(Debug)]
 pub struct MissingMatchArms {
-    pub file: HirFileId,
-    pub match_expr: AstPtr<ast::Expr>,
+    pub scrutinee_expr: InFile<AstPtr<ast::Expr>>,
     pub uncovered_patterns: String,
 }
 
 #[derive(Debug)]
 pub struct TypeMismatch {
-    // FIXME: add mismatches in patterns as well
-    pub expr: InFile<AstPtr<ast::Expr>>,
+    pub expr_or_pat: Either<InFile<AstPtr<ast::Expr>>, InFile<AstPtr<ast::Pat>>>,
     pub expected: Type,
     pub actual: Type,
 }
 
+#[derive(Debug)]
+pub struct NeedMut {
+    pub local: Local,
+    pub span: InFile<SyntaxNodePtr>,
+}
+
+#[derive(Debug)]
+pub struct UnusedMut {
+    pub local: Local,
+}
+
 pub use hir_ty::diagnostics::IncorrectCase;
diff --git a/src/tools/rust-analyzer/crates/hir/src/display.rs b/src/tools/rust-analyzer/crates/hir/src/display.rs
index 0d19420127f..5aae92efd19 100644
--- a/src/tools/rust-analyzer/crates/hir/src/display.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/display.rs
@@ -17,15 +17,23 @@ use hir_ty::{
 };
 
 use crate::{
-    Adt, Const, ConstParam, Enum, Field, Function, GenericParam, HasCrate, HasVisibility,
-    LifetimeParam, Macro, Module, Static, Struct, Trait, TyBuilder, Type, TypeAlias,
-    TypeOrConstParam, TypeParam, Union, Variant,
+    Adt, AsAssocItem, AssocItemContainer, Const, ConstParam, Enum, Field, Function, GenericParam,
+    HasCrate, HasVisibility, LifetimeParam, Macro, Module, Static, Struct, Trait, TraitAlias,
+    TyBuilder, Type, TypeAlias, TypeOrConstParam, TypeParam, Union, Variant,
 };
 
 impl HirDisplay for Function {
     fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
-        let data = f.db.function_data(self.id);
-        write_visibility(self.module(f.db).id, self.visibility(f.db), f)?;
+        let db = f.db;
+        let data = db.function_data(self.id);
+        let container = self.as_assoc_item(db).map(|it| it.container(db));
+        let mut module = self.module(db);
+        if let Some(AssocItemContainer::Impl(_)) = container {
+            // Block-local impls are "hoisted" to the nearest (non-block) module.
+            module = module.nearest_non_block_module(db);
+        }
+        let module_id = module.id;
+        write_visibility(module_id, self.visibility(db), f)?;
         if data.has_default_kw() {
             f.write_str("default ")?;
         }
@@ -35,7 +43,7 @@ impl HirDisplay for Function {
         if data.has_async_kw() {
             f.write_str("async ")?;
         }
-        if self.is_unsafe_to_call(f.db) {
+        if self.is_unsafe_to_call(db) {
             f.write_str("unsafe ")?;
         }
         if let Some(abi) = &data.abi {
@@ -50,7 +58,7 @@ impl HirDisplay for Function {
 
         let write_self_param = |ty: &TypeRef, f: &mut HirFormatter<'_>| match ty {
             TypeRef::Path(p) if p.is_self_type() => f.write_str("self"),
-            TypeRef::Reference(inner, lifetime, mut_) if matches!(&**inner,TypeRef::Path(p) if p.is_self_type()) =>
+            TypeRef::Reference(inner, lifetime, mut_) if matches!(&**inner, TypeRef::Path(p) if p.is_self_type()) =>
             {
                 f.write_char('&')?;
                 if let Some(lifetime) = lifetime {
@@ -442,8 +450,15 @@ fn write_where_clause(def: GenericDefId, f: &mut HirFormatter<'_>) -> Result<(),
 
 impl HirDisplay for Const {
     fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
-        write_visibility(self.module(f.db).id, self.visibility(f.db), f)?;
-        let data = f.db.const_data(self.id);
+        let db = f.db;
+        let container = self.as_assoc_item(db).map(|it| it.container(db));
+        let mut module = self.module(db);
+        if let Some(AssocItemContainer::Impl(_)) = container {
+            // Block-local impls are "hoisted" to the nearest (non-block) module.
+            module = module.nearest_non_block_module(db);
+        }
+        write_visibility(module.id, self.visibility(db), f)?;
+        let data = db.const_data(self.id);
         f.write_str("const ")?;
         match &data.name {
             Some(name) => write!(f, "{name}: ")?,
@@ -486,6 +501,22 @@ impl HirDisplay for Trait {
     }
 }
 
+impl HirDisplay for TraitAlias {
+    fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
+        write_visibility(self.module(f.db).id, self.visibility(f.db), f)?;
+        let data = f.db.trait_alias_data(self.id);
+        write!(f, "trait {}", data.name)?;
+        let def_id = GenericDefId::TraitAliasId(self.id);
+        write_generic_params(def_id, f)?;
+        f.write_str(" = ")?;
+        // FIXME: Currently we lower every bounds in a trait alias as a trait bound on `Self` i.e.
+        // `trait Foo = Bar` is stored and displayed as `trait Foo = where Self: Bar`, which might
+        // be less readable.
+        write_where_clause(def_id, f)?;
+        Ok(())
+    }
+}
+
 impl HirDisplay for TypeAlias {
     fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
         write_visibility(self.module(f.db).id, self.visibility(f.db), f)?;
diff --git a/src/tools/rust-analyzer/crates/hir/src/from_id.rs b/src/tools/rust-analyzer/crates/hir/src/from_id.rs
index f825a72c0f5..aaaa7abf386 100644
--- a/src/tools/rust-analyzer/crates/hir/src/from_id.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/from_id.rs
@@ -4,7 +4,7 @@
 //! are splitting the hir.
 
 use hir_def::{
-    expr::{LabelId, PatId},
+    expr::{BindingId, LabelId},
     AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, GenericDefId, GenericParamId,
     ModuleDefId, VariantId,
 };
@@ -37,6 +37,7 @@ from_id![
     (hir_def::EnumId, crate::Enum),
     (hir_def::TypeAliasId, crate::TypeAlias),
     (hir_def::TraitId, crate::Trait),
+    (hir_def::TraitAliasId, crate::TraitAlias),
     (hir_def::StaticId, crate::Static),
     (hir_def::ConstId, crate::Const),
     (hir_def::FunctionId, crate::Function),
@@ -110,6 +111,7 @@ impl From<ModuleDefId> for ModuleDef {
             ModuleDefId::ConstId(it) => ModuleDef::Const(it.into()),
             ModuleDefId::StaticId(it) => ModuleDef::Static(it.into()),
             ModuleDefId::TraitId(it) => ModuleDef::Trait(it.into()),
+            ModuleDefId::TraitAliasId(it) => ModuleDef::TraitAlias(it.into()),
             ModuleDefId::TypeAliasId(it) => ModuleDef::TypeAlias(it.into()),
             ModuleDefId::BuiltinType(it) => ModuleDef::BuiltinType(it.into()),
             ModuleDefId::MacroId(it) => ModuleDef::Macro(it.into()),
@@ -127,6 +129,7 @@ impl From<ModuleDef> for ModuleDefId {
             ModuleDef::Const(it) => ModuleDefId::ConstId(it.into()),
             ModuleDef::Static(it) => ModuleDefId::StaticId(it.into()),
             ModuleDef::Trait(it) => ModuleDefId::TraitId(it.into()),
+            ModuleDef::TraitAlias(it) => ModuleDefId::TraitAliasId(it.into()),
             ModuleDef::TypeAlias(it) => ModuleDefId::TypeAliasId(it.into()),
             ModuleDef::BuiltinType(it) => ModuleDefId::BuiltinType(it.into()),
             ModuleDef::Macro(it) => ModuleDefId::MacroId(it.into()),
@@ -172,6 +175,7 @@ impl From<GenericDef> for GenericDefId {
             GenericDef::Function(it) => GenericDefId::FunctionId(it.id),
             GenericDef::Adt(it) => GenericDefId::AdtId(it.into()),
             GenericDef::Trait(it) => GenericDefId::TraitId(it.id),
+            GenericDef::TraitAlias(it) => GenericDefId::TraitAliasId(it.id),
             GenericDef::TypeAlias(it) => GenericDefId::TypeAliasId(it.id),
             GenericDef::Impl(it) => GenericDefId::ImplId(it.id),
             GenericDef::Variant(it) => GenericDefId::EnumVariantId(it.into()),
@@ -186,6 +190,7 @@ impl From<GenericDefId> for GenericDef {
             GenericDefId::FunctionId(it) => GenericDef::Function(it.into()),
             GenericDefId::AdtId(it) => GenericDef::Adt(it.into()),
             GenericDefId::TraitId(it) => GenericDef::Trait(it.into()),
+            GenericDefId::TraitAliasId(it) => GenericDef::TraitAlias(it.into()),
             GenericDefId::TypeAliasId(it) => GenericDef::TypeAlias(it.into()),
             GenericDefId::ImplId(it) => GenericDef::Impl(it.into()),
             GenericDefId::EnumVariantId(it) => GenericDef::Variant(it.into()),
@@ -246,9 +251,9 @@ impl From<AssocItem> for GenericDefId {
     }
 }
 
-impl From<(DefWithBodyId, PatId)> for Local {
-    fn from((parent, pat_id): (DefWithBodyId, PatId)) -> Self {
-        Local { parent, pat_id }
+impl From<(DefWithBodyId, BindingId)> for Local {
+    fn from((parent, binding_id): (DefWithBodyId, BindingId)) -> Self {
+        Local { parent, binding_id }
     }
 }
 
diff --git a/src/tools/rust-analyzer/crates/hir/src/has_source.rs b/src/tools/rust-analyzer/crates/hir/src/has_source.rs
index f8b01db3e32..9f6b5c0a9fc 100644
--- a/src/tools/rust-analyzer/crates/hir/src/has_source.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/has_source.rs
@@ -10,8 +10,9 @@ use hir_expand::InFile;
 use syntax::ast;
 
 use crate::{
-    db::HirDatabase, Adt, Const, Enum, Field, FieldSource, Function, Impl, LifetimeParam, Macro,
-    Module, Static, Struct, Trait, TypeAlias, TypeOrConstParam, Union, Variant,
+    db::HirDatabase, Adt, Const, Enum, Field, FieldSource, Function, Impl, LifetimeParam,
+    LocalSource, Macro, Module, Static, Struct, Trait, TraitAlias, TypeAlias, TypeOrConstParam,
+    Union, Variant,
 };
 
 pub trait HasSource {
@@ -122,6 +123,12 @@ impl HasSource for Trait {
         Some(self.id.lookup(db.upcast()).source(db.upcast()))
     }
 }
+impl HasSource for TraitAlias {
+    type Ast = ast::TraitAlias;
+    fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
+        Some(self.id.lookup(db.upcast()).source(db.upcast()))
+    }
+}
 impl HasSource for TypeAlias {
     type Ast = ast::TypeAlias;
     fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
@@ -158,7 +165,7 @@ impl HasSource for Impl {
 }
 
 impl HasSource for TypeOrConstParam {
-    type Ast = Either<ast::TypeOrConstParam, ast::Trait>;
+    type Ast = Either<ast::TypeOrConstParam, ast::TraitOrAlias>;
     fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
         let child_source = self.id.parent.child_source(db.upcast());
         Some(child_source.map(|it| it[self.id.local_id].clone()))
@@ -172,3 +179,11 @@ impl HasSource for LifetimeParam {
         Some(child_source.map(|it| it[self.id.local_id].clone()))
     }
 }
+
+impl HasSource for LocalSource {
+    type Ast = Either<ast::IdentPat, ast::SelfParam>;
+
+    fn source(self, _: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
+        Some(self.source)
+    }
+}
diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs
index 2cb4ed2c335..25c07a2fbd3 100644
--- a/src/tools/rust-analyzer/crates/hir/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs
@@ -41,34 +41,34 @@ use either::Either;
 use hir_def::{
     adt::VariantData,
     body::{BodyDiagnostic, SyntheticSyntax},
-    expr::{BindingAnnotation, ExprOrPatId, LabelId, Pat, PatId},
-    generics::{TypeOrConstParamData, TypeParamProvenance},
+    expr::{BindingAnnotation, BindingId, ExprOrPatId, LabelId, Pat},
+    generics::{LifetimeParamData, TypeOrConstParamData, TypeParamProvenance},
     item_tree::ItemTreeNode,
     lang_item::{LangItem, LangItemTarget},
     layout::{Layout, LayoutError, ReprOptions},
-    nameres::{self, diagnostics::DefDiagnostic},
+    nameres::{self, diagnostics::DefDiagnostic, ModuleOrigin},
     per_ns::PerNs,
     resolver::{HasResolver, Resolver},
     src::HasSource as _,
-    type_ref::ConstScalar,
     AdtId, AssocItemId, AssocItemLoc, AttrDefId, ConstId, ConstParamId, DefWithBodyId, EnumId,
     EnumVariantId, FunctionId, GenericDefId, HasModule, ImplId, ItemContainerId, LifetimeParamId,
     LocalEnumVariantId, LocalFieldId, Lookup, MacroExpander, MacroId, ModuleId, StaticId, StructId,
-    TraitId, TypeAliasId, TypeOrConstParamId, TypeParamId, UnionId,
+    TraitAliasId, TraitId, TypeAliasId, TypeOrConstParamId, TypeParamId, UnionId,
 };
 use hir_expand::{name::name, MacroCallKind};
 use hir_ty::{
     all_super_traits, autoderef,
-    consteval::{unknown_const_as_generic, ComputedExpr, ConstEvalError, ConstExt},
+    consteval::{try_const_usize, unknown_const_as_generic, ConstEvalError, ConstExt},
     diagnostics::BodyValidationDiagnostic,
+    display::HexifiedConst,
     layout::layout_of_ty,
     method_resolution::{self, TyFingerprint},
+    mir::{self, interpret_mir},
     primitive::UintTy,
     traits::FnTrait,
     AliasTy, CallableDefId, CallableSig, Canonical, CanonicalVarKinds, Cast, ClosureId,
-    ConcreteConst, ConstValue, GenericArgData, Interner, ParamKind, QuantifiedWhereClause, Scalar,
-    Substitution, TraitEnvironment, TraitRefExt, Ty, TyBuilder, TyDefId, TyExt, TyKind,
-    WhereClause,
+    GenericArgData, Interner, ParamKind, QuantifiedWhereClause, Scalar, Substitution,
+    TraitEnvironment, TraitRefExt, Ty, TyBuilder, TyDefId, TyExt, TyKind, WhereClause,
 };
 use itertools::Itertools;
 use nameres::diagnostics::DefDiagnosticKind;
@@ -77,7 +77,7 @@ use rustc_hash::FxHashSet;
 use stdx::{impl_from, never};
 use syntax::{
     ast::{self, HasAttrs as _, HasDocComments, HasName},
-    AstNode, AstPtr, SmolStr, SyntaxNodePtr, TextRange, T,
+    AstNode, AstPtr, SmolStr, SyntaxNode, SyntaxNodePtr, TextRange, T,
 };
 
 use crate::db::{DefDatabase, HirDatabase};
@@ -85,12 +85,12 @@ use crate::db::{DefDatabase, HirDatabase};
 pub use crate::{
     attrs::{HasAttrs, Namespace},
     diagnostics::{
-        AnyDiagnostic, BreakOutsideOfLoop, InactiveCode, IncorrectCase, InvalidDeriveTarget,
-        MacroError, MalformedDerive, MismatchedArgCount, MissingFields, MissingMatchArms,
-        MissingUnsafe, NoSuchField, PrivateAssocItem, PrivateField,
+        AnyDiagnostic, BreakOutsideOfLoop, ExpectedFunction, InactiveCode, IncorrectCase,
+        InvalidDeriveTarget, MacroError, MalformedDerive, MismatchedArgCount, MissingFields,
+        MissingMatchArms, MissingUnsafe, NeedMut, NoSuchField, PrivateAssocItem, PrivateField,
         ReplaceFilterMapNextWithFindMap, TypeMismatch, UnimplementedBuiltinMacro,
-        UnresolvedExternCrate, UnresolvedImport, UnresolvedMacroCall, UnresolvedModule,
-        UnresolvedProcMacro,
+        UnresolvedExternCrate, UnresolvedField, UnresolvedImport, UnresolvedMacroCall,
+        UnresolvedMethodCall, UnresolvedModule, UnresolvedProcMacro, UnusedMut,
     },
     has_source::HasSource,
     semantics::{PathResolution, Semantics, SemanticsScope, TypeInfo, VisibleTraits},
@@ -130,6 +130,7 @@ pub use {
     },
     hir_ty::{
         display::{HirDisplay, HirDisplayError, HirWrite},
+        mir::MirEvalError,
         PointerCast, Safety,
     },
 };
@@ -272,6 +273,7 @@ pub enum ModuleDef {
     Const(Const),
     Static(Static),
     Trait(Trait),
+    TraitAlias(TraitAlias),
     TypeAlias(TypeAlias),
     BuiltinType(BuiltinType),
     Macro(Macro),
@@ -284,6 +286,7 @@ impl_from!(
     Const,
     Static,
     Trait,
+    TraitAlias,
     TypeAlias,
     BuiltinType,
     Macro
@@ -310,6 +313,7 @@ impl ModuleDef {
             ModuleDef::Const(it) => Some(it.module(db)),
             ModuleDef::Static(it) => Some(it.module(db)),
             ModuleDef::Trait(it) => Some(it.module(db)),
+            ModuleDef::TraitAlias(it) => Some(it.module(db)),
             ModuleDef::TypeAlias(it) => Some(it.module(db)),
             ModuleDef::Macro(it) => Some(it.module(db)),
             ModuleDef::BuiltinType(_) => None,
@@ -338,6 +342,7 @@ impl ModuleDef {
             ModuleDef::Const(it) => it.name(db)?,
             ModuleDef::Adt(it) => it.name(db),
             ModuleDef::Trait(it) => it.name(db),
+            ModuleDef::TraitAlias(it) => it.name(db),
             ModuleDef::Function(it) => it.name(db),
             ModuleDef::Variant(it) => it.name(db),
             ModuleDef::TypeAlias(it) => it.name(db),
@@ -356,6 +361,7 @@ impl ModuleDef {
                 Adt::Union(it) => it.id.into(),
             },
             ModuleDef::Trait(it) => it.id.into(),
+            ModuleDef::TraitAlias(it) => it.id.into(),
             ModuleDef::Function(it) => it.id.into(),
             ModuleDef::TypeAlias(it) => it.id.into(),
             ModuleDef::Module(it) => it.id.into(),
@@ -398,6 +404,7 @@ impl ModuleDef {
             ModuleDef::Module(_)
             | ModuleDef::Adt(_)
             | ModuleDef::Trait(_)
+            | ModuleDef::TraitAlias(_)
             | ModuleDef::TypeAlias(_)
             | ModuleDef::Macro(_)
             | ModuleDef::BuiltinType(_) => None,
@@ -413,6 +420,7 @@ impl ModuleDef {
             ModuleDef::Const(it) => it.attrs(db),
             ModuleDef::Static(it) => it.attrs(db),
             ModuleDef::Trait(it) => it.attrs(db),
+            ModuleDef::TraitAlias(it) => it.attrs(db),
             ModuleDef::TypeAlias(it) => it.attrs(db),
             ModuleDef::Macro(it) => it.attrs(db),
             ModuleDef::BuiltinType(_) => return None,
@@ -429,6 +437,7 @@ impl HasVisibility for ModuleDef {
             ModuleDef::Const(it) => it.visibility(db),
             ModuleDef::Static(it) => it.visibility(db),
             ModuleDef::Trait(it) => it.visibility(db),
+            ModuleDef::TraitAlias(it) => it.visibility(db),
             ModuleDef::TypeAlias(it) => it.visibility(db),
             ModuleDef::Variant(it) => it.visibility(db),
             ModuleDef::Macro(it) => it.visibility(db),
@@ -488,6 +497,20 @@ impl Module {
         Some(Module { id: def_map.module_id(parent_id) })
     }
 
+    /// Finds nearest non-block ancestor `Module` (`self` included).
+    pub fn nearest_non_block_module(self, db: &dyn HirDatabase) -> Module {
+        let mut id = self.id;
+        loop {
+            let def_map = id.def_map(db.upcast());
+            let origin = def_map[id.local_id].origin;
+            if matches!(origin, ModuleOrigin::BlockExpr { .. }) {
+                id = id.containing_module(db.upcast()).expect("block without parent module")
+            } else {
+                return Module { id };
+            }
+        }
+    }
+
     pub fn path_to_root(self, db: &dyn HirDatabase) -> Vec<Module> {
         let mut res = vec![self];
         let mut curr = self;
@@ -1092,8 +1115,8 @@ impl Variant {
         self.source(db)?.value.expr()
     }
 
-    pub fn eval(self, db: &dyn HirDatabase) -> Result<ComputedExpr, ConstEvalError> {
-        db.const_eval_variant(self.into())
+    pub fn eval(self, db: &dyn HirDatabase) -> Result<i128, ConstEvalError> {
+        db.const_eval_discriminant(self.into())
     }
 }
 
@@ -1170,6 +1193,25 @@ impl Adt {
         }
     }
 
+    /// Returns the lifetime of the DataType
+    pub fn lifetime(&self, db: &dyn HirDatabase) -> Option<LifetimeParamData> {
+        let resolver = match self {
+            Adt::Struct(s) => s.id.resolver(db.upcast()),
+            Adt::Union(u) => u.id.resolver(db.upcast()),
+            Adt::Enum(e) => e.id.resolver(db.upcast()),
+        };
+        resolver
+            .generic_params()
+            .and_then(|gp| {
+                (&gp.lifetimes)
+                    .iter()
+                    // there should only be a single lifetime
+                    // but `Arena` requires to use an iterator
+                    .nth(0)
+            })
+            .map(|arena| arena.1.clone())
+    }
+
     pub fn as_enum(&self) -> Option<Enum> {
         if let Self::Enum(v) = self {
             Some(*v)
@@ -1285,6 +1327,15 @@ impl DefWithBody {
         body.pretty_print(db.upcast(), self.id())
     }
 
+    /// A textual representation of the MIR of this def's body for debugging purposes.
+    pub fn debug_mir(self, db: &dyn HirDatabase) -> String {
+        let body = db.mir_body(self.id());
+        match body {
+            Ok(body) => body.pretty_print(db),
+            Err(e) => format!("error:\n{e:?}"),
+        }
+    }
+
     pub fn diagnostics(self, db: &dyn HirDatabase, acc: &mut Vec<AnyDiagnostic>) {
         let krate = self.module(db).id.krate();
 
@@ -1334,42 +1385,35 @@ impl DefWithBody {
 
         let infer = db.infer(self.into());
         let source_map = Lazy::new(|| db.body_with_source_map(self.into()).1);
+        let expr_syntax = |expr| source_map.expr_syntax(expr).expect("unexpected synthetic");
         for d in &infer.diagnostics {
             match d {
-                hir_ty::InferenceDiagnostic::NoSuchField { expr } => {
-                    let field = source_map.field_syntax(*expr);
+                &hir_ty::InferenceDiagnostic::NoSuchField { expr } => {
+                    let field = source_map.field_syntax(expr);
                     acc.push(NoSuchField { field }.into())
                 }
-                &hir_ty::InferenceDiagnostic::BreakOutsideOfLoop { expr, is_break } => {
-                    let expr = source_map
-                        .expr_syntax(expr)
-                        .expect("break outside of loop in synthetic syntax");
-                    acc.push(BreakOutsideOfLoop { expr, is_break }.into())
+                &hir_ty::InferenceDiagnostic::BreakOutsideOfLoop {
+                    expr,
+                    is_break,
+                    bad_value_break,
+                } => {
+                    let expr = expr_syntax(expr);
+                    acc.push(BreakOutsideOfLoop { expr, is_break, bad_value_break }.into())
                 }
-                hir_ty::InferenceDiagnostic::MismatchedArgCount { call_expr, expected, found } => {
-                    match source_map.expr_syntax(*call_expr) {
-                        Ok(source_ptr) => acc.push(
-                            MismatchedArgCount {
-                                call_expr: source_ptr,
-                                expected: *expected,
-                                found: *found,
-                            }
+                &hir_ty::InferenceDiagnostic::MismatchedArgCount { call_expr, expected, found } => {
+                    acc.push(
+                        MismatchedArgCount { call_expr: expr_syntax(call_expr), expected, found }
                             .into(),
-                        ),
-                        Err(SyntheticSyntax) => (),
-                    }
+                    )
                 }
                 &hir_ty::InferenceDiagnostic::PrivateField { expr, field } => {
-                    let expr = source_map.expr_syntax(expr).expect("unexpected synthetic");
+                    let expr = expr_syntax(expr);
                     let field = field.into();
                     acc.push(PrivateField { expr, field }.into())
                 }
                 &hir_ty::InferenceDiagnostic::PrivateAssocItem { id, item } => {
                     let expr_or_pat = match id {
-                        ExprOrPatId::ExprId(expr) => source_map
-                            .expr_syntax(expr)
-                            .expect("unexpected synthetic")
-                            .map(Either::Left),
+                        ExprOrPatId::ExprId(expr) => expr_syntax(expr).map(Either::Left),
                         ExprOrPatId::PatId(pat) => source_map
                             .pat_syntax(pat)
                             .expect("unexpected synthetic")
@@ -1378,16 +1422,76 @@ impl DefWithBody {
                     let item = item.into();
                     acc.push(PrivateAssocItem { expr_or_pat, item }.into())
                 }
+                hir_ty::InferenceDiagnostic::ExpectedFunction { call_expr, found } => {
+                    let call_expr = expr_syntax(*call_expr);
+
+                    acc.push(
+                        ExpectedFunction {
+                            call: call_expr,
+                            found: Type::new(db, DefWithBodyId::from(self), found.clone()),
+                        }
+                        .into(),
+                    )
+                }
+                hir_ty::InferenceDiagnostic::UnresolvedField {
+                    expr,
+                    receiver,
+                    name,
+                    method_with_same_name_exists,
+                } => {
+                    let expr = expr_syntax(*expr);
+
+                    acc.push(
+                        UnresolvedField {
+                            expr,
+                            name: name.clone(),
+                            receiver: Type::new(db, DefWithBodyId::from(self), receiver.clone()),
+                            method_with_same_name_exists: *method_with_same_name_exists,
+                        }
+                        .into(),
+                    )
+                }
+                hir_ty::InferenceDiagnostic::UnresolvedMethodCall {
+                    expr,
+                    receiver,
+                    name,
+                    field_with_same_name,
+                } => {
+                    let expr = expr_syntax(*expr);
+
+                    acc.push(
+                        UnresolvedMethodCall {
+                            expr,
+                            name: name.clone(),
+                            receiver: Type::new(db, DefWithBodyId::from(self), receiver.clone()),
+                            field_with_same_name: field_with_same_name
+                                .clone()
+                                .map(|ty| Type::new(db, DefWithBodyId::from(self), ty)),
+                        }
+                        .into(),
+                    )
+                }
             }
         }
-        for (expr, mismatch) in infer.expr_type_mismatches() {
-            let expr = match source_map.expr_syntax(expr) {
-                Ok(expr) => expr,
-                Err(SyntheticSyntax) => continue,
+        for (pat_or_expr, mismatch) in infer.type_mismatches() {
+            let expr_or_pat = match pat_or_expr {
+                ExprOrPatId::ExprId(expr) => source_map.expr_syntax(expr).map(Either::Left),
+                // FIXME: Re-enable these once we have less false positives
+                ExprOrPatId::PatId(_pat) => continue,
+                #[allow(unreachable_patterns)]
+                ExprOrPatId::PatId(pat) => source_map.pat_syntax(pat).map(Either::Right),
             };
+            let expr_or_pat = match expr_or_pat {
+                Ok(Either::Left(expr)) => Either::Left(expr),
+                Ok(Either::Right(InFile { file_id, value: Either::Left(pat) })) => {
+                    Either::Right(InFile { file_id, value: pat })
+                }
+                Ok(Either::Right(_)) | Err(SyntheticSyntax) => continue,
+            };
+
             acc.push(
                 TypeMismatch {
-                    expr,
+                    expr_or_pat,
                     expected: Type::new(db, DefWithBodyId::from(self), mismatch.expected.clone()),
                     actual: Type::new(db, DefWithBodyId::from(self), mismatch.actual.clone()),
                 }
@@ -1405,6 +1509,41 @@ impl DefWithBody {
             }
         }
 
+        let hir_body = db.body(self.into());
+
+        if let Ok(borrowck_result) = db.borrowck(self.into()) {
+            let mir_body = &borrowck_result.mir_body;
+            let mol = &borrowck_result.mutability_of_locals;
+            for (binding_id, _) in hir_body.bindings.iter() {
+                let need_mut = &mol[mir_body.binding_locals[binding_id]];
+                let local = Local { parent: self.into(), binding_id };
+                match (need_mut, local.is_mut(db)) {
+                    (mir::MutabilityReason::Mut { .. }, true)
+                    | (mir::MutabilityReason::Not, false) => (),
+                    (mir::MutabilityReason::Mut { spans }, false) => {
+                        for span in spans {
+                            let span: InFile<SyntaxNodePtr> = match span {
+                                mir::MirSpan::ExprId(e) => match source_map.expr_syntax(*e) {
+                                    Ok(s) => s.map(|x| x.into()),
+                                    Err(_) => continue,
+                                },
+                                mir::MirSpan::PatId(p) => match source_map.pat_syntax(*p) {
+                                    Ok(s) => s.map(|x| match x {
+                                        Either::Left(e) => e.into(),
+                                        Either::Right(e) => e.into(),
+                                    }),
+                                    Err(_) => continue,
+                                },
+                                mir::MirSpan::Unknown => continue,
+                            };
+                            acc.push(NeedMut { local, span }.into());
+                        }
+                    }
+                    (mir::MutabilityReason::Not, true) => acc.push(UnusedMut { local }.into()),
+                }
+            }
+        }
+
         for diagnostic in BodyValidationDiagnostic::collect(db, self.into()) {
             match diagnostic {
                 BodyValidationDiagnostic::RecordMissingFields {
@@ -1489,11 +1628,13 @@ impl DefWithBody {
                             if let ast::Expr::MatchExpr(match_expr) =
                                 &source_ptr.value.to_node(&root)
                             {
-                                if let Some(match_expr) = match_expr.expr() {
+                                if let Some(scrut_expr) = match_expr.expr() {
                                     acc.push(
                                         MissingMatchArms {
-                                            file: source_ptr.file_id,
-                                            match_expr: AstPtr::new(&match_expr),
+                                            scrutinee_expr: InFile::new(
+                                                source_ptr.file_id,
+                                                AstPtr::new(&scrut_expr),
+                                            ),
                                             uncovered_patterns,
                                         }
                                         .into(),
@@ -1582,6 +1723,10 @@ impl Function {
             .collect()
     }
 
+    pub fn num_params(self, db: &dyn HirDatabase) -> usize {
+        db.function_data(self.id).params.len()
+    }
+
     pub fn method_params(self, db: &dyn HirDatabase) -> Option<Vec<Param>> {
         if self.self_param(db).is_none() {
             return None;
@@ -1639,6 +1784,14 @@ impl Function {
         let def_map = db.crate_def_map(loc.krate(db).into());
         def_map.fn_as_proc_macro(self.id).map(|id| Macro { id: id.into() })
     }
+
+    pub fn eval(self, db: &dyn HirDatabase) -> Result<(), MirEvalError> {
+        let body = db
+            .mir_body(self.id.into())
+            .map_err(|e| MirEvalError::MirLowerError(self.id.into(), e))?;
+        interpret_mir(db, &body, false)?;
+        Ok(())
+    }
 }
 
 // Note: logically, this belongs to `hir_ty`, but we are not using it there yet.
@@ -1679,8 +1832,8 @@ impl Param {
         let parent = DefWithBodyId::FunctionId(self.func.into());
         let body = db.body(parent);
         let pat_id = body.params[self.idx];
-        if let Pat::Bind { .. } = &body[pat_id] {
-            Some(Local { parent, pat_id: body.params[self.idx] })
+        if let Pat::Bind { id, .. } = &body[pat_id] {
+            Some(Local { parent, binding_id: *id })
         } else {
             None
         }
@@ -1781,8 +1934,18 @@ impl Const {
         Type::new_with_resolver_inner(db, &resolver, ty)
     }
 
-    pub fn eval(self, db: &dyn HirDatabase) -> Result<ComputedExpr, ConstEvalError> {
-        db.const_eval(self.id)
+    pub fn render_eval(self, db: &dyn HirDatabase) -> Result<String, ConstEvalError> {
+        let c = db.const_eval(self.id)?;
+        let r = format!("{}", HexifiedConst(c).display(db));
+        // We want to see things like `<utf8-error>` and `<layout-error>` as they are probably bug in our
+        // implementation, but there is no need to show things like `<enum-not-supported>` or `<ref-not-supported>` to
+        // the user.
+        if r.contains("not-supported>") {
+            return Err(ConstEvalError::MirEvalError(MirEvalError::NotSupported(
+                "rendering complex constants".to_string(),
+            )));
+        }
+        return Ok(r);
     }
 }
 
@@ -1894,6 +2057,27 @@ impl HasVisibility for Trait {
 }
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct TraitAlias {
+    pub(crate) id: TraitAliasId,
+}
+
+impl TraitAlias {
+    pub fn module(self, db: &dyn HirDatabase) -> Module {
+        Module { id: self.id.lookup(db.upcast()).container }
+    }
+
+    pub fn name(self, db: &dyn HirDatabase) -> Name {
+        db.trait_alias_data(self.id).name.clone()
+    }
+}
+
+impl HasVisibility for TraitAlias {
+    fn visibility(&self, db: &dyn HirDatabase) -> Visibility {
+        db.trait_alias_data(self.id).visibility.resolve(db.upcast(), &self.id.resolver(db.upcast()))
+    }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub struct TypeAlias {
     pub(crate) id: TypeAliasId,
 }
@@ -2265,6 +2449,7 @@ pub enum GenericDef {
     Function(Function),
     Adt(Adt),
     Trait(Trait),
+    TraitAlias(TraitAlias),
     TypeAlias(TypeAlias),
     Impl(Impl),
     // enum variants cannot have generics themselves, but their parent enums
@@ -2277,6 +2462,7 @@ impl_from!(
     Function,
     Adt(Struct, Enum, Union),
     Trait,
+    TraitAlias,
     TypeAlias,
     Impl,
     Variant,
@@ -2317,20 +2503,53 @@ impl GenericDef {
 }
 
 /// A single local definition.
-///
-/// If the definition of this is part of a "MultiLocal", that is a local that has multiple declarations due to or-patterns
-/// then this only references a single one of those.
-/// To retrieve the other locals you should use [`Local::associated_locals`]
 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
 pub struct Local {
     pub(crate) parent: DefWithBodyId,
-    pub(crate) pat_id: PatId,
+    pub(crate) binding_id: BindingId,
+}
+
+pub struct LocalSource {
+    pub local: Local,
+    pub source: InFile<Either<ast::IdentPat, ast::SelfParam>>,
+}
+
+impl LocalSource {
+    pub fn as_ident_pat(&self) -> Option<&ast::IdentPat> {
+        match &self.source.value {
+            Either::Left(x) => Some(x),
+            Either::Right(_) => None,
+        }
+    }
+
+    pub fn into_ident_pat(self) -> Option<ast::IdentPat> {
+        match self.source.value {
+            Either::Left(x) => Some(x),
+            Either::Right(_) => None,
+        }
+    }
+
+    pub fn original_file(&self, db: &dyn HirDatabase) -> FileId {
+        self.source.file_id.original_file(db.upcast())
+    }
+
+    pub fn name(&self) -> Option<ast::Name> {
+        self.source.value.name()
+    }
+
+    pub fn syntax(&self) -> &SyntaxNode {
+        self.source.value.syntax()
+    }
+
+    pub fn syntax_ptr(self) -> InFile<SyntaxNodePtr> {
+        self.source.map(|x| SyntaxNodePtr::new(x.syntax()))
+    }
 }
 
 impl Local {
     pub fn is_param(self, db: &dyn HirDatabase) -> bool {
-        let src = self.source(db);
-        match src.value {
+        let src = self.primary_source(db);
+        match src.source.value {
             Either::Left(pat) => pat
                 .syntax()
                 .ancestors()
@@ -2350,13 +2569,7 @@ impl Local {
 
     pub fn name(self, db: &dyn HirDatabase) -> Name {
         let body = db.body(self.parent);
-        match &body[self.pat_id] {
-            Pat::Bind { name, .. } => name.clone(),
-            _ => {
-                stdx::never!("hir::Local is missing a name!");
-                Name::missing()
-            }
-        }
+        body[self.binding_id].name.clone()
     }
 
     pub fn is_self(self, db: &dyn HirDatabase) -> bool {
@@ -2365,15 +2578,12 @@ impl Local {
 
     pub fn is_mut(self, db: &dyn HirDatabase) -> bool {
         let body = db.body(self.parent);
-        matches!(&body[self.pat_id], Pat::Bind { mode: BindingAnnotation::Mutable, .. })
+        body[self.binding_id].mode == BindingAnnotation::Mutable
     }
 
     pub fn is_ref(self, db: &dyn HirDatabase) -> bool {
         let body = db.body(self.parent);
-        matches!(
-            &body[self.pat_id],
-            Pat::Bind { mode: BindingAnnotation::Ref | BindingAnnotation::RefMut, .. }
-        )
+        matches!(body[self.binding_id].mode, BindingAnnotation::Ref | BindingAnnotation::RefMut)
     }
 
     pub fn parent(self, _db: &dyn HirDatabase) -> DefWithBody {
@@ -2387,34 +2597,33 @@ impl Local {
     pub fn ty(self, db: &dyn HirDatabase) -> Type {
         let def = self.parent;
         let infer = db.infer(def);
-        let ty = infer[self.pat_id].clone();
+        let ty = infer[self.binding_id].clone();
         Type::new(db, def, ty)
     }
 
-    pub fn associated_locals(self, db: &dyn HirDatabase) -> Box<[Local]> {
-        let body = db.body(self.parent);
-        body.ident_patterns_for(&self.pat_id)
+    /// All definitions for this local. Example: `let (a$0, _) | (_, a$0) = x;`
+    pub fn sources(self, db: &dyn HirDatabase) -> Vec<LocalSource> {
+        let (body, source_map) = db.body_with_source_map(self.parent);
+        body[self.binding_id]
+            .definitions
             .iter()
-            .map(|&pat_id| Local { parent: self.parent, pat_id })
+            .map(|&definition| {
+                let src = source_map.pat_syntax(definition).unwrap(); // Hmm...
+                let root = src.file_syntax(db.upcast());
+                src.map(|ast| match ast {
+                    // Suspicious unwrap
+                    Either::Left(it) => Either::Left(it.cast().unwrap().to_node(&root)),
+                    Either::Right(it) => Either::Right(it.to_node(&root)),
+                })
+            })
+            .map(|source| LocalSource { local: self, source })
             .collect()
     }
 
-    /// If this local is part of a multi-local, retrieve the representative local.
-    /// That is the local that references are being resolved to.
-    pub fn representative(self, db: &dyn HirDatabase) -> Local {
-        let body = db.body(self.parent);
-        Local { pat_id: body.pattern_representative(self.pat_id), ..self }
-    }
-
-    pub fn source(self, db: &dyn HirDatabase) -> InFile<Either<ast::IdentPat, ast::SelfParam>> {
-        let (_body, source_map) = db.body_with_source_map(self.parent);
-        let src = source_map.pat_syntax(self.pat_id).unwrap(); // Hmm...
-        let root = src.file_syntax(db.upcast());
-        src.map(|ast| match ast {
-            // Suspicious unwrap
-            Either::Left(it) => Either::Left(it.cast().unwrap().to_node(&root)),
-            Either::Right(it) => Either::Right(it.to_node(&root)),
-        })
+    /// The leftmost definition for this local. Example: `let (a$0, _) | (_, a) = x;`
+    pub fn primary_source(self, db: &dyn HirDatabase) -> LocalSource {
+        let all_sources = self.sources(db);
+        all_sources.into_iter().next().unwrap()
     }
 }
 
@@ -3190,6 +3399,14 @@ impl Type {
         matches!(self.ty.kind(Interner), TyKind::Raw(..))
     }
 
+    pub fn remove_raw_ptr(&self) -> Option<Type> {
+        if let TyKind::Raw(_, ty) = self.ty.kind(Interner) {
+            Some(self.derived(ty.clone()))
+        } else {
+            None
+        }
+    }
+
     pub fn contains_unknown(&self) -> bool {
         // FIXME: When we get rid of `ConstScalar::Unknown`, we can just look at precomputed
         // `TypeFlags` in `TyData`.
@@ -3260,12 +3477,7 @@ impl Type {
 
     pub fn as_array(&self, _db: &dyn HirDatabase) -> Option<(Type, usize)> {
         if let TyKind::Array(ty, len) = &self.ty.kind(Interner) {
-            match len.data(Interner).value {
-                ConstValue::Concrete(ConcreteConst { interned: ConstScalar::UInt(len) }) => {
-                    Some((self.derived(ty.clone()), len as usize))
-                }
-                _ => None,
-            }
+            try_const_usize(len).map(|x| (self.derived(ty.clone()), x as usize))
         } else {
             None
         }
@@ -3321,6 +3533,24 @@ impl Type {
         }
     }
 
+    /// Iterates its type arguments
+    ///
+    /// It iterates the actual type arguments when concrete types are used
+    /// and otherwise the generic names.
+    /// It does not include `const` arguments.
+    ///
+    /// For code, such as:
+    /// ```text
+    /// struct Foo<T, U>
+    ///
+    /// impl<U> Foo<String, U>
+    /// ```
+    ///
+    /// It iterates:
+    /// ```text
+    /// - "String"
+    /// - "U"
+    /// ```
     pub fn type_arguments(&self) -> impl Iterator<Item = Type> + '_ {
         self.ty
             .strip_references()
@@ -3331,12 +3561,62 @@ impl Type {
             .map(move |ty| self.derived(ty))
     }
 
-    pub fn iterate_method_candidates<T>(
+    /// Iterates its type and const arguments
+    ///
+    /// It iterates the actual type and const arguments when concrete types
+    /// are used and otherwise the generic names.
+    ///
+    /// For code, such as:
+    /// ```text
+    /// struct Foo<T, const U: usize, const X: usize>
+    ///
+    /// impl<U> Foo<String, U, 12>
+    /// ```
+    ///
+    /// It iterates:
+    /// ```text
+    /// - "String"
+    /// - "U"
+    /// - "12"
+    /// ```
+    pub fn type_and_const_arguments<'a>(
+        &'a self,
+        db: &'a dyn HirDatabase,
+    ) -> impl Iterator<Item = SmolStr> + 'a {
+        self.ty
+            .strip_references()
+            .as_adt()
+            .into_iter()
+            .flat_map(|(_, substs)| substs.iter(Interner))
+            .filter_map(|arg| {
+                // arg can be either a `Ty` or `constant`
+                if let Some(ty) = arg.ty(Interner) {
+                    Some(SmolStr::new(ty.display(db).to_string()))
+                } else if let Some(const_) = arg.constant(Interner) {
+                    Some(SmolStr::new_inline(&const_.display(db).to_string()))
+                } else {
+                    None
+                }
+            })
+    }
+
+    /// Combines lifetime indicators, type and constant parameters into a single `Iterator`
+    pub fn generic_parameters<'a>(
+        &'a self,
+        db: &'a dyn HirDatabase,
+    ) -> impl Iterator<Item = SmolStr> + 'a {
+        // iterate the lifetime
+        self.as_adt()
+            .and_then(|a| a.lifetime(db).and_then(|lt| Some((&lt.name).to_smol_str())))
+            .into_iter()
+            // add the type and const paramaters
+            .chain(self.type_and_const_arguments(db))
+    }
+
+    pub fn iterate_method_candidates_with_traits<T>(
         &self,
         db: &dyn HirDatabase,
         scope: &SemanticsScope<'_>,
-        // FIXME this can be retrieved from `scope`, except autoimport uses this
-        // to specify a different set, so the method needs to be split
         traits_in_scope: &FxHashSet<TraitId>,
         with_local_impls: Option<Module>,
         name: Option<&Name>,
@@ -3364,6 +3644,24 @@ impl Type {
         slot
     }
 
+    pub fn iterate_method_candidates<T>(
+        &self,
+        db: &dyn HirDatabase,
+        scope: &SemanticsScope<'_>,
+        with_local_impls: Option<Module>,
+        name: Option<&Name>,
+        callback: impl FnMut(Function) -> Option<T>,
+    ) -> Option<T> {
+        self.iterate_method_candidates_with_traits(
+            db,
+            scope,
+            &scope.visible_traits().0,
+            with_local_impls,
+            name,
+            callback,
+        )
+    }
+
     fn iterate_method_candidates_dyn(
         &self,
         db: &dyn HirDatabase,
@@ -3632,11 +3930,13 @@ impl Type {
     }
 }
 
+// FIXME: Document this
 #[derive(Debug)]
 pub struct Callable {
     ty: Type,
     sig: CallableSig,
     callee: Callee,
+    /// Whether this is a method that was called with method call syntax.
     pub(crate) is_bound_method: bool,
 }
 
@@ -3670,14 +3970,14 @@ impl Callable {
             Other => CallableKind::Other,
         }
     }
-    pub fn receiver_param(&self, db: &dyn HirDatabase) -> Option<ast::SelfParam> {
+    pub fn receiver_param(&self, db: &dyn HirDatabase) -> Option<(ast::SelfParam, Type)> {
         let func = match self.callee {
             Callee::Def(CallableDefId::FunctionId(it)) if self.is_bound_method => it,
             _ => return None,
         };
         let src = func.lookup(db.upcast()).source(db.upcast());
         let param_list = src.value.param_list()?;
-        param_list.self_param()
+        Some((param_list.self_param()?, self.ty.derived(self.sig.params()[0].clone())))
     }
     pub fn n_params(&self) -> usize {
         self.sig.params().len() - if self.is_bound_method { 1 } else { 0 }
@@ -3936,6 +4236,12 @@ impl HasCrate for Trait {
     }
 }
 
+impl HasCrate for TraitAlias {
+    fn krate(&self, db: &dyn HirDatabase) -> Crate {
+        self.module(db).krate()
+    }
+}
+
 impl HasCrate for Static {
     fn krate(&self, db: &dyn HirDatabase) -> Crate {
         self.module(db).krate()
diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs
index 486b7ee62ed..8bd905d0113 100644
--- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs
@@ -12,7 +12,7 @@ use hir_def::{
     macro_id_to_def_id,
     resolver::{self, HasResolver, Resolver, TypeNs},
     type_ref::Mutability,
-    AsMacroCall, DefWithBodyId, FunctionId, MacroId, TraitId, VariantId,
+    AsMacroCall, DefWithBodyId, FieldId, FunctionId, MacroId, TraitId, VariantId,
 };
 use hir_expand::{
     db::AstDatabase,
@@ -68,7 +68,8 @@ impl PathResolution {
                 | ModuleDef::Function(_)
                 | ModuleDef::Module(_)
                 | ModuleDef::Static(_)
-                | ModuleDef::Trait(_),
+                | ModuleDef::Trait(_)
+                | ModuleDef::TraitAlias(_),
             ) => None,
             PathResolution::Def(ModuleDef::TypeAlias(alias)) => {
                 Some(TypeNs::TypeAliasId((*alias).into()))
@@ -365,6 +366,16 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
         self.imp.resolve_method_call(call).map(Function::from)
     }
 
+    /// Attempts to resolve this call expression as a method call falling back to resolving it as a field.
+    pub fn resolve_method_call_field_fallback(
+        &self,
+        call: &ast::MethodCallExpr,
+    ) -> Option<Either<Function, Field>> {
+        self.imp
+            .resolve_method_call_fallback(call)
+            .map(|it| it.map_left(Function::from).map_right(Field::from))
+    }
+
     pub fn resolve_await_to_poll(&self, await_expr: &ast::AwaitExpr) -> Option<Function> {
         self.imp.resolve_await_to_poll(await_expr).map(Function::from)
     }
@@ -527,8 +538,8 @@ impl<'db> SemanticsImpl<'db> {
     }
 
     fn expand_derive_as_pseudo_attr_macro(&self, attr: &ast::Attr) -> Option<SyntaxNode> {
-        let src = self.wrap_node_infile(attr.clone());
         let adt = attr.syntax().parent().and_then(ast::Adt::cast)?;
+        let src = self.wrap_node_infile(attr.clone());
         let call_id = self.with_ctx(|ctx| {
             ctx.attr_to_derive_macro_call(src.with_value(&adt), src).map(|(_, it, _)| it)
         })?;
@@ -1092,7 +1103,10 @@ impl<'db> SemanticsImpl<'db> {
                     let kind = match adjust.kind {
                         hir_ty::Adjust::NeverToAny => Adjust::NeverToAny,
                         hir_ty::Adjust::Deref(Some(hir_ty::OverloadedDeref(m))) => {
-                            Adjust::Deref(Some(OverloadedDeref(mutability(m))))
+                            // FIXME: Should we handle unknown mutability better?
+                            Adjust::Deref(Some(OverloadedDeref(
+                                m.map(mutability).unwrap_or(Mutability::Shared),
+                            )))
                         }
                         hir_ty::Adjust::Deref(None) => Adjust::Deref(None),
                         hir_ty::Adjust::Borrow(hir_ty::AutoBorrow::RawPtr(m)) => {
@@ -1145,6 +1159,13 @@ impl<'db> SemanticsImpl<'db> {
         self.analyze(call.syntax())?.resolve_method_call(self.db, call)
     }
 
+    fn resolve_method_call_fallback(
+        &self,
+        call: &ast::MethodCallExpr,
+    ) -> Option<Either<FunctionId, FieldId>> {
+        self.analyze(call.syntax())?.resolve_method_call_fallback(self.db, call)
+    }
+
     fn resolve_await_to_poll(&self, await_expr: &ast::AwaitExpr) -> Option<FunctionId> {
         self.analyze(await_expr.syntax())?.resolve_await_to_poll(self.db, await_expr)
     }
@@ -1330,6 +1351,7 @@ impl<'db> SemanticsImpl<'db> {
                 })
             }
             ChildContainer::TraitId(it) => it.resolver(self.db.upcast()),
+            ChildContainer::TraitAliasId(it) => it.resolver(self.db.upcast()),
             ChildContainer::ImplId(it) => it.resolver(self.db.upcast()),
             ChildContainer::ModuleId(it) => it.resolver(self.db.upcast()),
             ChildContainer::EnumId(it) => it.resolver(self.db.upcast()),
@@ -1556,6 +1578,7 @@ to_def_impls![
     (crate::Enum, ast::Enum, enum_to_def),
     (crate::Union, ast::Union, union_to_def),
     (crate::Trait, ast::Trait, trait_to_def),
+    (crate::TraitAlias, ast::TraitAlias, trait_alias_to_def),
     (crate::Impl, ast::Impl, impl_to_def),
     (crate::TypeAlias, ast::TypeAlias, type_alias_to_def),
     (crate::Const, ast::Const, const_to_def),
@@ -1634,8 +1657,8 @@ impl<'a> SemanticsScope<'a> {
                     resolver::ScopeDef::ImplSelfType(it) => ScopeDef::ImplSelfType(it.into()),
                     resolver::ScopeDef::AdtSelfType(it) => ScopeDef::AdtSelfType(it.into()),
                     resolver::ScopeDef::GenericParam(id) => ScopeDef::GenericParam(id.into()),
-                    resolver::ScopeDef::Local(pat_id) => match self.resolver.body_owner() {
-                        Some(parent) => ScopeDef::Local(Local { parent, pat_id }),
+                    resolver::ScopeDef::Local(binding_id) => match self.resolver.body_owner() {
+                        Some(parent) => ScopeDef::Local(Local { parent, binding_id }),
                         None => continue,
                     },
                     resolver::ScopeDef::Label(label_id) => match self.resolver.body_owner() {
@@ -1673,6 +1696,7 @@ impl<'a> SemanticsScope<'a> {
     }
 }
 
+#[derive(Debug)]
 pub struct VisibleTraits(pub FxHashSet<TraitId>);
 
 impl ops::Deref for VisibleTraits {
diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs b/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs
index 2b5bfda1d43..f6f8c9a250f 100644
--- a/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs
@@ -89,16 +89,16 @@ use base_db::FileId;
 use hir_def::{
     child_by_source::ChildBySource,
     dyn_map::DynMap,
-    expr::{LabelId, PatId},
+    expr::{BindingId, LabelId},
     keys::{self, Key},
     AdtId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, FieldId, FunctionId,
     GenericDefId, GenericParamId, ImplId, LifetimeParamId, MacroId, ModuleId, StaticId, StructId,
-    TraitId, TypeAliasId, TypeParamId, UnionId, VariantId,
+    TraitAliasId, TraitId, TypeAliasId, TypeParamId, UnionId, VariantId,
 };
 use hir_expand::{attrs::AttrId, name::AsName, HirFileId, MacroCallId};
 use rustc_hash::FxHashMap;
 use smallvec::SmallVec;
-use stdx::impl_from;
+use stdx::{impl_from, never};
 use syntax::{
     ast::{self, HasName},
     AstNode, SyntaxNode,
@@ -159,6 +159,12 @@ impl SourceToDefCtx<'_, '_> {
     pub(super) fn trait_to_def(&mut self, src: InFile<ast::Trait>) -> Option<TraitId> {
         self.to_def(src, keys::TRAIT)
     }
+    pub(super) fn trait_alias_to_def(
+        &mut self,
+        src: InFile<ast::TraitAlias>,
+    ) -> Option<TraitAliasId> {
+        self.to_def(src, keys::TRAIT_ALIAS)
+    }
     pub(super) fn impl_to_def(&mut self, src: InFile<ast::Impl>) -> Option<ImplId> {
         self.to_def(src, keys::IMPL)
     }
@@ -210,14 +216,14 @@ impl SourceToDefCtx<'_, '_> {
     pub(super) fn bind_pat_to_def(
         &mut self,
         src: InFile<ast::IdentPat>,
-    ) -> Option<(DefWithBodyId, PatId)> {
+    ) -> Option<(DefWithBodyId, BindingId)> {
         let container = self.find_pat_or_label_container(src.syntax())?;
         let (body, source_map) = self.db.body_with_source_map(container);
         let src = src.map(ast::Pat::from);
         let pat_id = source_map.node_pat(src.as_ref())?;
         // the pattern could resolve to a constant, verify that that is not the case
-        if let crate::Pat::Bind { .. } = body[pat_id] {
-            Some((container, pat_id))
+        if let crate::Pat::Bind { id, .. } = body[pat_id] {
+            Some((container, id))
         } else {
             None
         }
@@ -225,11 +231,16 @@ impl SourceToDefCtx<'_, '_> {
     pub(super) fn self_param_to_def(
         &mut self,
         src: InFile<ast::SelfParam>,
-    ) -> Option<(DefWithBodyId, PatId)> {
+    ) -> Option<(DefWithBodyId, BindingId)> {
         let container = self.find_pat_or_label_container(src.syntax())?;
-        let (_body, source_map) = self.db.body_with_source_map(container);
+        let (body, source_map) = self.db.body_with_source_map(container);
         let pat_id = source_map.node_self_param(src.as_ref())?;
-        Some((container, pat_id))
+        if let crate::Pat::Bind { id, .. } = body[pat_id] {
+            Some((container, id))
+        } else {
+            never!();
+            None
+        }
     }
     pub(super) fn label_to_def(
         &mut self,
@@ -353,6 +364,9 @@ impl SourceToDefCtx<'_, '_> {
             match item {
                 ast::Item::Module(it) => self.module_to_def(container.with_value(it))?.into(),
                 ast::Item::Trait(it) => self.trait_to_def(container.with_value(it))?.into(),
+                ast::Item::TraitAlias(it) => {
+                    self.trait_alias_to_def(container.with_value(it))?.into()
+                }
                 ast::Item::Impl(it) => self.impl_to_def(container.with_value(it))?.into(),
                 ast::Item::Enum(it) => self.enum_to_def(container.with_value(it))?.into(),
                 ast::Item::TypeAlias(it) => {
@@ -400,6 +414,9 @@ impl SourceToDefCtx<'_, '_> {
                 ast::Item::Struct(it) => self.struct_to_def(InFile::new(file_id, it))?.into(),
                 ast::Item::Enum(it) => self.enum_to_def(InFile::new(file_id, it))?.into(),
                 ast::Item::Trait(it) => self.trait_to_def(InFile::new(file_id, it))?.into(),
+                ast::Item::TraitAlias(it) => {
+                    self.trait_alias_to_def(InFile::new(file_id, it))?.into()
+                }
                 ast::Item::TypeAlias(it) => {
                     self.type_alias_to_def(InFile::new(file_id, it))?.into()
                 }
@@ -435,6 +452,7 @@ pub(crate) enum ChildContainer {
     DefWithBodyId(DefWithBodyId),
     ModuleId(ModuleId),
     TraitId(TraitId),
+    TraitAliasId(TraitAliasId),
     ImplId(ImplId),
     EnumId(EnumId),
     VariantId(VariantId),
@@ -447,6 +465,7 @@ impl_from! {
     DefWithBodyId,
     ModuleId,
     TraitId,
+    TraitAliasId,
     ImplId,
     EnumId,
     VariantId,
@@ -462,6 +481,7 @@ impl ChildContainer {
             ChildContainer::DefWithBodyId(it) => it.child_by_source(db, file_id),
             ChildContainer::ModuleId(it) => it.child_by_source(db, file_id),
             ChildContainer::TraitId(it) => it.child_by_source(db, file_id),
+            ChildContainer::TraitAliasId(_) => DynMap::default(),
             ChildContainer::ImplId(it) => it.child_by_source(db, file_id),
             ChildContainer::EnumId(it) => it.child_by_source(db, file_id),
             ChildContainer::VariantId(it) => it.child_by_source(db, file_id),
diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
index 3b39e9fa919..133fa810d66 100644
--- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
@@ -10,6 +10,7 @@ use std::{
     sync::Arc,
 };
 
+use either::Either;
 use hir_def::{
     body::{
         self,
@@ -51,7 +52,7 @@ use syntax::{
 use crate::{
     db::HirDatabase, semantics::PathResolution, Adt, AssocItem, BindingMode, BuiltinAttr,
     BuiltinType, Callable, Const, DeriveHelper, Field, Function, Local, Macro, ModuleDef, Static,
-    Struct, ToolModule, Trait, Type, TypeAlias, Variant,
+    Struct, ToolModule, Trait, TraitAlias, Type, TypeAlias, Variant,
 };
 
 /// `SourceAnalyzer` is a convenience wrapper which exposes HIR API in terms of
@@ -266,6 +267,21 @@ impl SourceAnalyzer {
         Some(self.resolve_impl_method_or_trait_def(db, f_in_trait, substs))
     }
 
+    pub(crate) fn resolve_method_call_fallback(
+        &self,
+        db: &dyn HirDatabase,
+        call: &ast::MethodCallExpr,
+    ) -> Option<Either<FunctionId, FieldId>> {
+        let expr_id = self.expr_id(db, &call.clone().into())?;
+        let inference_result = self.infer.as_ref()?;
+        match inference_result.method_resolution(expr_id) {
+            Some((f_in_trait, substs)) => {
+                Some(Either::Left(self.resolve_impl_method_or_trait_def(db, f_in_trait, substs)))
+            }
+            None => inference_result.field_resolution(expr_id).map(Either::Right),
+        }
+    }
+
     pub(crate) fn resolve_await_to_poll(
         &self,
         db: &dyn HirDatabase,
@@ -406,8 +422,8 @@ impl SourceAnalyzer {
             // Shorthand syntax, resolve to the local
             let path = ModPath::from_segments(PathKind::Plain, once(local_name.clone()));
             match self.resolver.resolve_path_in_value_ns_fully(db.upcast(), &path) {
-                Some(ValueNs::LocalBinding(pat_id)) => {
-                    Some(Local { pat_id, parent: self.resolver.body_owner()? })
+                Some(ValueNs::LocalBinding(binding_id)) => {
+                    Some(Local { binding_id, parent: self.resolver.body_owner()? })
                 }
                 _ => None,
             }
@@ -791,7 +807,7 @@ impl SourceAnalyzer {
             || Arc::new(hir_ty::TraitEnvironment::empty(krate)),
             |d| db.trait_environment(d),
         );
-        method_resolution::lookup_impl_method(db, env, func, substs)
+        method_resolution::lookup_impl_method(db, env, func, substs).0
     }
 
     fn resolve_impl_const_or_trait_def(
@@ -809,7 +825,7 @@ impl SourceAnalyzer {
             || Arc::new(hir_ty::TraitEnvironment::empty(krate)),
             |d| db.trait_environment(d),
         );
-        method_resolution::lookup_impl_const(db, env, const_id, subs)
+        method_resolution::lookup_impl_const(db, env, const_id, subs).0
     }
 
     fn lang_trait_fn(
@@ -943,17 +959,17 @@ fn resolve_hir_path_(
                 res.map(|ty_ns| (ty_ns, path.segments().first()))
             }
             None => {
-                let (ty, remaining) =
+                let (ty, remaining_idx) =
                     resolver.resolve_path_in_type_ns(db.upcast(), path.mod_path())?;
-                match remaining {
-                    Some(remaining) if remaining > 1 => {
-                        if remaining + 1 == path.segments().len() {
+                match remaining_idx {
+                    Some(remaining_idx) => {
+                        if remaining_idx + 1 == path.segments().len() {
                             Some((ty, path.segments().last()))
                         } else {
                             None
                         }
                     }
-                    _ => Some((ty, path.segments().get(1))),
+                    None => Some((ty, None)),
                 }
             }
         }?;
@@ -978,6 +994,7 @@ fn resolve_hir_path_(
             TypeNs::TypeAliasId(it) => PathResolution::Def(TypeAlias::from(it).into()),
             TypeNs::BuiltinType(it) => PathResolution::Def(BuiltinType::from(it).into()),
             TypeNs::TraitId(it) => PathResolution::Def(Trait::from(it).into()),
+            TypeNs::TraitAliasId(it) => PathResolution::Def(TraitAlias::from(it).into()),
         };
         match unresolved {
             Some(unresolved) => resolver
@@ -1001,8 +1018,8 @@ fn resolve_hir_path_(
     let values = || {
         resolver.resolve_path_in_value_ns_fully(db.upcast(), path.mod_path()).and_then(|val| {
             let res = match val {
-                ValueNs::LocalBinding(pat_id) => {
-                    let var = Local { parent: body_owner?, pat_id };
+                ValueNs::LocalBinding(binding_id) => {
+                    let var = Local { parent: body_owner?, binding_id };
                     PathResolution::Local(var)
                 }
                 ValueNs::FunctionId(it) => PathResolution::Def(Function::from(it).into()),
@@ -1065,6 +1082,7 @@ fn resolve_hir_path_qualifier(
             TypeNs::TypeAliasId(it) => PathResolution::Def(TypeAlias::from(it).into()),
             TypeNs::BuiltinType(it) => PathResolution::Def(BuiltinType::from(it).into()),
             TypeNs::TraitId(it) => PathResolution::Def(Trait::from(it).into()),
+            TypeNs::TraitAliasId(it) => PathResolution::Def(TraitAlias::from(it).into()),
         })
         .or_else(|| {
             resolver
diff --git a/src/tools/rust-analyzer/crates/hir/src/symbols.rs b/src/tools/rust-analyzer/crates/hir/src/symbols.rs
index fd78decda4e..a9afa1c6f45 100644
--- a/src/tools/rust-analyzer/crates/hir/src/symbols.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/symbols.rs
@@ -68,6 +68,7 @@ pub enum FileSymbolKind {
     Static,
     Struct,
     Trait,
+    TraitAlias,
     TypeAlias,
     Union,
 }
@@ -153,6 +154,9 @@ impl<'a> SymbolCollector<'a> {
                     self.push_decl(id, FileSymbolKind::Trait);
                     self.collect_from_trait(id);
                 }
+                ModuleDefId::TraitAliasId(id) => {
+                    self.push_decl(id, FileSymbolKind::TraitAlias);
+                }
                 ModuleDefId::TypeAliasId(id) => {
                     self.push_decl_assoc(id, FileSymbolKind::TypeAlias);
                 }
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_explicit_type.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_explicit_type.rs
index 0057f439f1a..785ae3d09c6 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_explicit_type.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_explicit_type.rs
@@ -211,10 +211,8 @@ fn main() {
         check_assist_not_applicable(
             add_explicit_type,
             r#"
-//- minicore: option
-
 fn main() {
-    let $0l = [0.0; Some(2).unwrap()];
+    let $0l = [0.0; unresolved_function(5)];
 }
 "#,
         );
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs
index f32ef2d59d8..9e1d9a702a1 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs
@@ -157,19 +157,12 @@ fn is_ref_and_impls_iter_method(
     let iter_trait = FamousDefs(sema, krate).core_iter_Iterator()?;
 
     let has_wanted_method = ty
-        .iterate_method_candidates(
-            sema.db,
-            &scope,
-            &scope.visible_traits().0,
-            None,
-            Some(&wanted_method),
-            |func| {
-                if func.ret_type(sema.db).impls_trait(sema.db, iter_trait, &[]) {
-                    return Some(());
-                }
-                None
-            },
-        )
+        .iterate_method_candidates(sema.db, &scope, None, Some(&wanted_method), |func| {
+            if func.ret_type(sema.db).impls_trait(sema.db, iter_trait, &[]) {
+                return Some(());
+            }
+            None
+        })
         .is_some();
     if !has_wanted_method {
         return None;
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs
index 65c2479e9f2..7f2c01772ba 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs
@@ -1,6 +1,6 @@
 use ide_db::defs::{Definition, NameRefClass};
 use syntax::{
-    ast::{self, HasName},
+    ast::{self, HasName, Name},
     ted, AstNode, SyntaxNode,
 };
 
@@ -48,7 +48,7 @@ pub(crate) fn convert_match_to_let_else(acc: &mut Assists, ctx: &AssistContext<'
         other => format!("{{ {other} }}"),
     };
     let extracting_arm_pat = extracting_arm.pat()?;
-    let extracted_variable = find_extracted_variable(ctx, &extracting_arm)?;
+    let extracted_variable_positions = find_extracted_variable(ctx, &extracting_arm)?;
 
     acc.add(
         AssistId("convert_match_to_let_else", AssistKind::RefactorRewrite),
@@ -56,7 +56,7 @@ pub(crate) fn convert_match_to_let_else(acc: &mut Assists, ctx: &AssistContext<'
         let_stmt.syntax().text_range(),
         |builder| {
             let extracting_arm_pat =
-                rename_variable(&extracting_arm_pat, extracted_variable, binding);
+                rename_variable(&extracting_arm_pat, &extracted_variable_positions, binding);
             builder.replace(
                 let_stmt.syntax().text_range(),
                 format!("let {extracting_arm_pat} = {initializer_expr} else {diverging_arm_expr};"),
@@ -95,14 +95,15 @@ fn find_arms(
 }
 
 // Given an extracting arm, find the extracted variable.
-fn find_extracted_variable(ctx: &AssistContext<'_>, arm: &ast::MatchArm) -> Option<ast::Name> {
+fn find_extracted_variable(ctx: &AssistContext<'_>, arm: &ast::MatchArm) -> Option<Vec<Name>> {
     match arm.expr()? {
         ast::Expr::PathExpr(path) => {
             let name_ref = path.syntax().descendants().find_map(ast::NameRef::cast)?;
             match NameRefClass::classify(&ctx.sema, &name_ref)? {
                 NameRefClass::Definition(Definition::Local(local)) => {
-                    let source = local.source(ctx.db()).value.left()?;
-                    Some(source.name()?)
+                    let source =
+                        local.sources(ctx.db()).into_iter().map(|x| x.into_ident_pat()?.name());
+                    source.collect()
                 }
                 _ => None,
             }
@@ -115,27 +116,34 @@ fn find_extracted_variable(ctx: &AssistContext<'_>, arm: &ast::MatchArm) -> Opti
 }
 
 // Rename `extracted` with `binding` in `pat`.
-fn rename_variable(pat: &ast::Pat, extracted: ast::Name, binding: ast::Pat) -> SyntaxNode {
+fn rename_variable(pat: &ast::Pat, extracted: &[Name], binding: ast::Pat) -> SyntaxNode {
     let syntax = pat.syntax().clone_for_update();
-    let extracted_syntax = syntax.covering_element(extracted.syntax().text_range());
-
-    // If `extracted` variable is a record field, we should rename it to `binding`,
-    // otherwise we just need to replace `extracted` with `binding`.
-
-    if let Some(record_pat_field) = extracted_syntax.ancestors().find_map(ast::RecordPatField::cast)
-    {
-        if let Some(name_ref) = record_pat_field.field_name() {
-            ted::replace(
-                record_pat_field.syntax(),
-                ast::make::record_pat_field(ast::make::name_ref(&name_ref.text()), binding)
+    let extracted = extracted
+        .iter()
+        .map(|e| syntax.covering_element(e.syntax().text_range()))
+        .collect::<Vec<_>>();
+    for extracted_syntax in extracted {
+        // If `extracted` variable is a record field, we should rename it to `binding`,
+        // otherwise we just need to replace `extracted` with `binding`.
+
+        if let Some(record_pat_field) =
+            extracted_syntax.ancestors().find_map(ast::RecordPatField::cast)
+        {
+            if let Some(name_ref) = record_pat_field.field_name() {
+                ted::replace(
+                    record_pat_field.syntax(),
+                    ast::make::record_pat_field(
+                        ast::make::name_ref(&name_ref.text()),
+                        binding.clone(),
+                    )
                     .syntax()
                     .clone_for_update(),
-            );
+                );
+            }
+        } else {
+            ted::replace(extracted_syntax, binding.clone().syntax().clone_for_update());
         }
-    } else {
-        ted::replace(extracted_syntax, binding.syntax().clone_for_update());
     }
-
     syntax
 }
 
@@ -163,6 +171,39 @@ fn foo(opt: Option<()>) {
     }
 
     #[test]
+    fn or_pattern_multiple_binding() {
+        check_assist(
+            convert_match_to_let_else,
+            r#"
+//- minicore: option
+enum Foo {
+    A(u32),
+    B(u32),
+    C(String),
+}
+
+fn foo(opt: Option<Foo>) -> Result<u32, ()> {
+    let va$0lue = match opt {
+        Some(Foo::A(it) | Foo::B(it)) => it,
+        _ => return Err(()),
+    };
+}
+    "#,
+            r#"
+enum Foo {
+    A(u32),
+    B(u32),
+    C(String),
+}
+
+fn foo(opt: Option<Foo>) -> Result<u32, ()> {
+    let Some(Foo::A(value) | Foo::B(value)) = opt else { return Err(()) };
+}
+    "#,
+        );
+    }
+
+    #[test]
     fn should_not_be_applicable_if_extracting_arm_is_not_an_identity_expr() {
         cov_mark::check_count!(extracting_arm_is_not_an_identity_expr, 2);
         check_assist_not_applicable(
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs
index e04a1dabb2c..0b90c9ba34f 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs
@@ -3,7 +3,8 @@ use std::iter;
 use ast::make;
 use either::Either;
 use hir::{
-    HasSource, HirDisplay, InFile, Local, ModuleDef, PathResolution, Semantics, TypeInfo, TypeParam,
+    HasSource, HirDisplay, InFile, Local, LocalSource, ModuleDef, PathResolution, Semantics,
+    TypeInfo, TypeParam,
 };
 use ide_db::{
     defs::{Definition, NameRefClass},
@@ -710,7 +711,7 @@ impl FunctionBody {
                     ) => local_ref,
                     _ => return,
                 };
-            let InFile { file_id, value } = local_ref.source(sema.db);
+            let InFile { file_id, value } = local_ref.primary_source(sema.db).source;
             // locals defined inside macros are not relevant to us
             if !file_id.is_macro() {
                 match value {
@@ -972,11 +973,11 @@ impl FunctionBody {
         locals: impl Iterator<Item = Local>,
     ) -> Vec<Param> {
         locals
-            .map(|local| (local, local.source(ctx.db())))
+            .map(|local| (local, local.primary_source(ctx.db())))
             .filter(|(_, src)| is_defined_outside_of_body(ctx, self, src))
-            .filter_map(|(local, src)| match src.value {
-                Either::Left(src) => Some((local, src)),
-                Either::Right(_) => {
+            .filter_map(|(local, src)| match src.into_ident_pat() {
+                Some(src) => Some((local, src)),
+                None => {
                     stdx::never!(false, "Local::is_self returned false, but source is SelfParam");
                     None
                 }
@@ -1238,17 +1239,9 @@ fn local_outlives_body(
 fn is_defined_outside_of_body(
     ctx: &AssistContext<'_>,
     body: &FunctionBody,
-    src: &hir::InFile<Either<ast::IdentPat, ast::SelfParam>>,
+    src: &LocalSource,
 ) -> bool {
-    src.file_id.original_file(ctx.db()) == ctx.file_id()
-        && !body.contains_node(either_syntax(&src.value))
-}
-
-fn either_syntax(value: &Either<ast::IdentPat, ast::SelfParam>) -> &SyntaxNode {
-    match value {
-        Either::Left(pat) => pat.syntax(),
-        Either::Right(it) => it.syntax(),
-    }
+    src.original_file(ctx.db()) == ctx.file_id() && !body.contains_node(src.syntax())
 }
 
 /// find where to put extracted function definition
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs
index a738deffb95..16356141288 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs
@@ -287,7 +287,7 @@ fn foo() {
             extract_variable,
             r"
 fn foo() {
-    $0{ let x = 0; x }$0
+    $0{ let x = 0; x }$0;
     something_else();
 }",
             r"
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs
index d9e00435ecf..4c61678eab4 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs
@@ -192,6 +192,10 @@ fn target_data_for_def(
             target_name = Some(t.name(db));
             offset_target_and_file_id(db, t)?
         }
+        hir::ModuleDef::TraitAlias(t) => {
+            target_name = Some(t.name(db));
+            offset_target_and_file_id(db, t)?
+        }
         hir::ModuleDef::TypeAlias(t) => {
             target_name = Some(t.name(db));
             offset_target_and_file_id(db, t)?
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs
index 45b27a63ce2..eef037f9875 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs
@@ -5,6 +5,7 @@ use ide_db::{
     base_db::FileId,
     defs::{Definition, NameRefClass},
     famous_defs::FamousDefs,
+    helpers::is_editable_crate,
     path_transform::PathTransform,
     FxHashMap, FxHashSet, RootDatabase, SnippetCap,
 };
@@ -65,6 +66,13 @@ fn gen_fn(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
     let fn_name = &*name_ref.text();
     let TargetInfo { target_module, adt_name, target, file, insert_offset } =
         fn_target_info(ctx, path, &call, fn_name)?;
+
+    if let Some(m) = target_module {
+        if !is_editable_crate(m.krate(), ctx.db()) {
+            return None;
+        }
+    }
+
     let function_builder = FunctionBuilder::from_call(ctx, &call, fn_name, target_module, target)?;
     let text_range = call.syntax().text_range();
     let label = format!("Generate {} function", function_builder.fn_name);
@@ -141,12 +149,11 @@ fn gen_method(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
     let receiver_ty = ctx.sema.type_of_expr(&call.receiver()?)?.original().strip_references();
     let adt = receiver_ty.as_adt()?;
 
-    let current_module = ctx.sema.scope(call.syntax())?.module();
     let target_module = adt.module(ctx.sema.db);
-
-    if current_module.krate() != target_module.krate() {
+    if !is_editable_crate(target_module.krate(), ctx.db()) {
         return None;
     }
+
     let (impl_, file) = get_adt_source(ctx, &adt, fn_name.text().as_str())?;
     let (target, insert_offset) = get_method_target(ctx, &impl_, &adt)?;
 
@@ -253,7 +260,7 @@ struct FunctionBuilder {
     params: ast::ParamList,
     ret_type: Option<ast::RetType>,
     should_focus_return_type: bool,
-    needs_pub: bool,
+    visibility: Visibility,
     is_async: bool,
 }
 
@@ -264,12 +271,14 @@ impl FunctionBuilder {
         ctx: &AssistContext<'_>,
         call: &ast::CallExpr,
         fn_name: &str,
-        target_module: Option<hir::Module>,
+        target_module: Option<Module>,
         target: GeneratedFunctionTarget,
     ) -> Option<Self> {
-        let needs_pub = target_module.is_some();
         let target_module =
             target_module.or_else(|| ctx.sema.scope(target.syntax()).map(|it| it.module()))?;
+
+        let current_module = ctx.sema.scope(call.syntax())?.module();
+        let visibility = calculate_necessary_visibility(current_module, target_module, ctx);
         let fn_name = make::name(fn_name);
         let mut necessary_generic_params = FxHashSet::default();
         let params = fn_args(
@@ -300,7 +309,7 @@ impl FunctionBuilder {
             params,
             ret_type,
             should_focus_return_type,
-            needs_pub,
+            visibility,
             is_async,
         })
     }
@@ -313,8 +322,9 @@ impl FunctionBuilder {
         target_module: Module,
         target: GeneratedFunctionTarget,
     ) -> Option<Self> {
-        let needs_pub =
-            !module_is_descendant(&ctx.sema.scope(call.syntax())?.module(), &target_module, ctx);
+        let current_module = ctx.sema.scope(call.syntax())?.module();
+        let visibility = calculate_necessary_visibility(current_module, target_module, ctx);
+
         let fn_name = make::name(&name.text());
         let mut necessary_generic_params = FxHashSet::default();
         necessary_generic_params.extend(receiver_ty.generic_params(ctx.db()));
@@ -346,7 +356,7 @@ impl FunctionBuilder {
             params,
             ret_type,
             should_focus_return_type,
-            needs_pub,
+            visibility,
             is_async,
         })
     }
@@ -354,7 +364,11 @@ impl FunctionBuilder {
     fn render(self, is_method: bool) -> FunctionTemplate {
         let placeholder_expr = make::ext::expr_todo();
         let fn_body = make::block_expr(vec![], Some(placeholder_expr));
-        let visibility = if self.needs_pub { Some(make::visibility_pub_crate()) } else { None };
+        let visibility = match self.visibility {
+            Visibility::None => None,
+            Visibility::Crate => Some(make::visibility_pub_crate()),
+            Visibility::Pub => Some(make::visibility_pub()),
+        };
         let mut fn_def = make::fn_(
             visibility,
             self.fn_name,
@@ -527,7 +541,7 @@ impl GeneratedFunctionTarget {
 /// Computes parameter list for the generated function.
 fn fn_args(
     ctx: &AssistContext<'_>,
-    target_module: hir::Module,
+    target_module: Module,
     call: ast::CallableExpr,
     necessary_generic_params: &mut FxHashSet<hir::GenericParam>,
 ) -> Option<ast::ParamList> {
@@ -957,13 +971,13 @@ fn fn_arg_name(sema: &Semantics<'_, RootDatabase>, arg_expr: &ast::Expr) -> Stri
 
 fn fn_arg_type(
     ctx: &AssistContext<'_>,
-    target_module: hir::Module,
+    target_module: Module,
     fn_arg: &ast::Expr,
     generic_params: &mut FxHashSet<hir::GenericParam>,
 ) -> String {
     fn maybe_displayed_type(
         ctx: &AssistContext<'_>,
-        target_module: hir::Module,
+        target_module: Module,
         fn_arg: &ast::Expr,
         generic_params: &mut FxHashSet<hir::GenericParam>,
     ) -> Option<String> {
@@ -1048,16 +1062,29 @@ fn next_space_for_fn_in_impl(impl_: &ast::Impl) -> Option<GeneratedFunctionTarge
     }
 }
 
-fn module_is_descendant(module: &hir::Module, ans: &hir::Module, ctx: &AssistContext<'_>) -> bool {
-    if module == ans {
-        return true;
-    }
-    for c in ans.children(ctx.sema.db) {
-        if module_is_descendant(module, &c, ctx) {
-            return true;
-        }
+#[derive(Clone, Copy)]
+enum Visibility {
+    None,
+    Crate,
+    Pub,
+}
+
+fn calculate_necessary_visibility(
+    current_module: Module,
+    target_module: Module,
+    ctx: &AssistContext<'_>,
+) -> Visibility {
+    let db = ctx.db();
+    let current_module = current_module.nearest_non_block_module(db);
+    let target_module = target_module.nearest_non_block_module(db);
+
+    if target_module.krate() != current_module.krate() {
+        Visibility::Pub
+    } else if current_module.path_to_root(db).contains(&target_module) {
+        Visibility::None
+    } else {
+        Visibility::Crate
     }
-    false
 }
 
 // This is never intended to be used as a generic graph strucuture. If there's ever another need of
@@ -2656,4 +2683,79 @@ fn main() {
 ",
         )
     }
+
+    #[test]
+    fn applicable_in_different_local_crate() {
+        check_assist(
+            generate_function,
+            r"
+//- /lib.rs crate:lib new_source_root:local
+fn dummy() {}
+//- /main.rs crate:main deps:lib new_source_root:local
+fn main() {
+    lib::foo$0();
+}
+",
+            r"
+fn dummy() {}
+
+pub fn foo() ${0:-> _} {
+    todo!()
+}
+",
+        );
+    }
+
+    #[test]
+    fn applicable_in_different_local_crate_method() {
+        check_assist(
+            generate_function,
+            r"
+//- /lib.rs crate:lib new_source_root:local
+pub struct S;
+//- /main.rs crate:main deps:lib new_source_root:local
+fn main() {
+    lib::S.foo$0();
+}
+",
+            r"
+pub struct S;
+impl S {
+    pub fn foo(&self) ${0:-> _} {
+        todo!()
+    }
+}
+",
+        );
+    }
+
+    #[test]
+    fn not_applicable_in_different_library_crate() {
+        check_assist_not_applicable(
+            generate_function,
+            r"
+//- /lib.rs crate:lib new_source_root:library
+fn dummy() {}
+//- /main.rs crate:main deps:lib new_source_root:local
+fn main() {
+    lib::foo$0();
+}
+",
+        );
+    }
+
+    #[test]
+    fn not_applicable_in_different_library_crate_method() {
+        check_assist_not_applicable(
+            generate_function,
+            r"
+//- /lib.rs crate:lib new_source_root:library
+pub struct S;
+//- /main.rs crate:main deps:lib new_source_root:local
+fn main() {
+    lib::S.foo$0();
+}
+",
+        );
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_is_empty_from_len.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_is_empty_from_len.rs
index 9ce525ca375..44291861960 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_is_empty_from_len.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_is_empty_from_len.rs
@@ -95,14 +95,7 @@ fn get_impl_method(
 
     let scope = ctx.sema.scope(impl_.syntax())?;
     let ty = impl_def.self_ty(db);
-    ty.iterate_method_candidates(
-        db,
-        &scope,
-        &scope.visible_traits().0,
-        None,
-        Some(fn_name),
-        |func| Some(func),
-    )
+    ty.iterate_method_candidates(db, &scope, None, Some(fn_name), |func| Some(func))
 }
 
 #[cfg(test)]
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs
index 8d311262a75..e30a3e942c4 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs
@@ -1,5 +1,5 @@
 use ide_db::{
-    imports::import_assets::item_for_path_search, use_trivial_contructor::use_trivial_constructor,
+    imports::import_assets::item_for_path_search, use_trivial_constructor::use_trivial_constructor,
 };
 use itertools::Itertools;
 use stdx::format_to;
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_local_variable.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_local_variable.rs
index ce44100e34b..e69d1a29677 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_local_variable.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_local_variable.rs
@@ -1,4 +1,3 @@
-use either::Either;
 use hir::{PathResolution, Semantics};
 use ide_db::{
     base_db::FileId,
@@ -205,12 +204,14 @@ fn inline_usage(
         return None;
     }
 
-    // FIXME: Handle multiple local definitions
-    let bind_pat = match local.source(sema.db).value {
-        Either::Left(ident) => ident,
-        _ => return None,
+    let sources = local.sources(sema.db);
+    let [source] = sources.as_slice() else {
+        // Not applicable with locals with multiple definitions (i.e. or patterns)
+        return None;
     };
 
+    let bind_pat = source.as_ident_pat()?;
+
     let let_stmt = ast::LetStmt::cast(bind_pat.syntax().parent()?)?;
 
     let UsageSearchResult { mut references } = Definition::Local(local).usages(sema).all();
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs
index 52dd670ec2a..1361cdf00cc 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs
@@ -1,7 +1,7 @@
 use itertools::Itertools;
 use syntax::{
-    ast::{self, AstNode, AstToken},
-    match_ast, NodeOrToken, SyntaxElement, TextRange, TextSize, T,
+    ast::{self, make, AstNode, AstToken},
+    match_ast, ted, NodeOrToken, SyntaxElement, TextRange, TextSize, T,
 };
 
 use crate::{AssistContext, AssistId, AssistKind, Assists};
@@ -12,24 +12,28 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
 //
 // ```
 // fn main() {
-//     $0dbg!(92);
+//     let x = $0dbg!(42 * dbg!(4 + 2));$0
 // }
 // ```
 // ->
 // ```
 // fn main() {
-//     92;
+//     let x = 42 * (4 + 2);
 // }
 // ```
 pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
     let macro_calls = if ctx.has_empty_selection() {
-        vec![ctx.find_node_at_offset::<ast::MacroCall>()?]
+        vec![ctx.find_node_at_offset::<ast::MacroExpr>()?]
     } else {
         ctx.covering_element()
             .as_node()?
             .descendants()
             .filter(|node| ctx.selection_trimmed().contains_range(node.text_range()))
+            // When the selection exactly covers the macro call to be removed, `covering_element()`
+            // returns `ast::MacroCall` instead of its parent `ast::MacroExpr` that we want. So
+            // first try finding `ast::MacroCall`s and then retrieve their parent.
             .filter_map(ast::MacroCall::cast)
+            .filter_map(|it| it.syntax().parent().and_then(ast::MacroExpr::cast))
             .collect()
     };
 
@@ -44,14 +48,25 @@ pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<(
         "Remove dbg!()",
         ctx.selection_trimmed(),
         |builder| {
-            for (range, text) in replacements {
-                builder.replace(range, text);
+            for (range, expr) in replacements {
+                if let Some(expr) = expr {
+                    builder.replace(range, expr.to_string());
+                } else {
+                    builder.delete(range);
+                }
             }
         },
     )
 }
 
-fn compute_dbg_replacement(macro_call: ast::MacroCall) -> Option<(TextRange, String)> {
+/// Returns `None` when either
+/// - macro call is not `dbg!()`
+/// - any node inside `dbg!()` could not be parsed as an expression
+/// - (`macro_expr` has no parent - is that possible?)
+///
+/// Returns `Some(_, None)` when the macro call should just be removed.
+fn compute_dbg_replacement(macro_expr: ast::MacroExpr) -> Option<(TextRange, Option<ast::Expr>)> {
+    let macro_call = macro_expr.macro_call()?;
     let tt = macro_call.token_tree()?;
     let r_delim = NodeOrToken::Token(tt.right_delimiter_token()?);
     if macro_call.path()?.segment()?.name_ref()?.text() != "dbg"
@@ -68,20 +83,19 @@ fn compute_dbg_replacement(macro_call: ast::MacroCall) -> Option<(TextRange, Str
         .map(|mut tokens| syntax::hacks::parse_expr_from_str(&tokens.join("")))
         .collect::<Option<Vec<ast::Expr>>>()?;
 
-    let macro_expr = ast::MacroExpr::cast(macro_call.syntax().parent()?)?;
     let parent = macro_expr.syntax().parent()?;
     Some(match &*input_expressions {
         // dbg!()
         [] => {
             match_ast! {
                 match parent {
-                    ast::StmtList(__) => {
+                    ast::StmtList(_) => {
                         let range = macro_expr.syntax().text_range();
                         let range = match whitespace_start(macro_expr.syntax().prev_sibling_or_token()) {
                             Some(start) => range.cover_offset(start),
                             None => range,
                         };
-                        (range, String::new())
+                        (range, None)
                     },
                     ast::ExprStmt(it) => {
                         let range = it.syntax().text_range();
@@ -89,19 +103,23 @@ fn compute_dbg_replacement(macro_call: ast::MacroCall) -> Option<(TextRange, Str
                             Some(start) => range.cover_offset(start),
                             None => range,
                         };
-                        (range, String::new())
+                        (range, None)
                     },
-                    _ => (macro_call.syntax().text_range(), "()".to_owned())
+                    _ => (macro_call.syntax().text_range(), Some(make::expr_unit())),
                 }
             }
         }
         // dbg!(expr0)
         [expr] => {
+            // dbg!(expr, &parent);
             let wrap = match ast::Expr::cast(parent) {
                 Some(parent) => match (expr, parent) {
                     (ast::Expr::CastExpr(_), ast::Expr::CastExpr(_)) => false,
                     (
-                        ast::Expr::BoxExpr(_) | ast::Expr::PrefixExpr(_) | ast::Expr::RefExpr(_),
+                        ast::Expr::BoxExpr(_)
+                        | ast::Expr::PrefixExpr(_)
+                        | ast::Expr::RefExpr(_)
+                        | ast::Expr::MacroExpr(_),
                         ast::Expr::AwaitExpr(_)
                         | ast::Expr::CallExpr(_)
                         | ast::Expr::CastExpr(_)
@@ -112,7 +130,10 @@ fn compute_dbg_replacement(macro_call: ast::MacroCall) -> Option<(TextRange, Str
                         | ast::Expr::TryExpr(_),
                     ) => true,
                     (
-                        ast::Expr::BinExpr(_) | ast::Expr::CastExpr(_) | ast::Expr::RangeExpr(_),
+                        ast::Expr::BinExpr(_)
+                        | ast::Expr::CastExpr(_)
+                        | ast::Expr::RangeExpr(_)
+                        | ast::Expr::MacroExpr(_),
                         ast::Expr::AwaitExpr(_)
                         | ast::Expr::BinExpr(_)
                         | ast::Expr::CallExpr(_)
@@ -129,16 +150,61 @@ fn compute_dbg_replacement(macro_call: ast::MacroCall) -> Option<(TextRange, Str
                 },
                 None => false,
             };
-            (
-                macro_call.syntax().text_range(),
-                if wrap { format!("({expr})") } else { expr.to_string() },
-            )
+            let expr = replace_nested_dbgs(expr.clone());
+            let expr = if wrap { make::expr_paren(expr) } else { expr.clone_subtree() };
+            (macro_call.syntax().text_range(), Some(expr))
         }
         // dbg!(expr0, expr1, ...)
-        exprs => (macro_call.syntax().text_range(), format!("({})", exprs.iter().format(", "))),
+        exprs => {
+            let exprs = exprs.iter().cloned().map(replace_nested_dbgs);
+            let expr = make::expr_tuple(exprs);
+            (macro_call.syntax().text_range(), Some(expr))
+        }
     })
 }
 
+fn replace_nested_dbgs(expanded: ast::Expr) -> ast::Expr {
+    if let ast::Expr::MacroExpr(mac) = &expanded {
+        // Special-case when `expanded` itself is `dbg!()` since we cannot replace the whole tree
+        // with `ted`. It should be fairly rare as it means the user wrote `dbg!(dbg!(..))` but you
+        // never know how code ends up being!
+        let replaced = if let Some((_, expr_opt)) = compute_dbg_replacement(mac.clone()) {
+            match expr_opt {
+                Some(expr) => expr,
+                None => {
+                    stdx::never!("dbg! inside dbg! should not be just removed");
+                    expanded
+                }
+            }
+        } else {
+            expanded
+        };
+
+        return replaced;
+    }
+
+    let expanded = expanded.clone_for_update();
+
+    // We need to collect to avoid mutation during traversal.
+    let macro_exprs: Vec<_> =
+        expanded.syntax().descendants().filter_map(ast::MacroExpr::cast).collect();
+
+    for mac in macro_exprs {
+        let expr_opt = match compute_dbg_replacement(mac.clone()) {
+            Some((_, expr)) => expr,
+            None => continue,
+        };
+
+        if let Some(expr) = expr_opt {
+            ted::replace(mac.syntax(), expr.syntax().clone_for_update());
+        } else {
+            ted::remove(mac.syntax());
+        }
+    }
+
+    expanded
+}
+
 fn whitespace_start(it: Option<SyntaxElement>) -> Option<TextSize> {
     Some(it?.into_token().and_then(ast::Whitespace::cast)?.syntax().text_range().start())
 }
@@ -287,4 +353,32 @@ fn f() {
         check_assist_not_applicable(remove_dbg, r#"$0dbg$0!(0)"#);
         check_assist_not_applicable(remove_dbg, r#"$0dbg!(0$0)"#);
     }
+
+    #[test]
+    fn test_nested_dbg() {
+        check(
+            r#"$0let x = dbg!(dbg!(dbg!(dbg!(0 + 1)) * 2) + dbg!(3));$0"#,
+            r#"let x = ((0 + 1) * 2) + 3;"#,
+        );
+        check(r#"$0dbg!(10, dbg!(), dbg!(20, 30))$0"#, r#"(10, (), (20, 30))"#);
+    }
+
+    #[test]
+    fn test_multiple_nested_dbg() {
+        check(
+            r#"
+fn f() {
+    $0dbg!();
+    let x = dbg!(dbg!(dbg!(0 + 1)) + 2) + dbg!(3);
+    dbg!(10, dbg!(), dbg!(20, 30));$0
+}
+"#,
+            r#"
+fn f() {
+    let x = ((0 + 1) + 2) + 3;
+    (10, (), (20, 30));
+}
+"#,
+        );
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_if_let_with_match.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_if_let_with_match.rs
index 457559656a4..5e31d38fbd6 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_if_let_with_match.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_if_let_with_match.rs
@@ -102,9 +102,11 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext<'
         return None;
     }
 
+    let let_ = if pat_seen { " let" } else { "" };
+
     acc.add(
         AssistId("replace_if_let_with_match", AssistKind::RefactorRewrite),
-        "Replace if let with match",
+        format!("Replace if{let_} with match"),
         available_range,
         move |edit| {
             let match_expr = {
@@ -210,8 +212,17 @@ fn make_else_arm(
 // ```
 pub(crate) fn replace_match_with_if_let(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
     let match_expr: ast::MatchExpr = ctx.find_node_at_offset()?;
+    let match_arm_list = match_expr.match_arm_list()?;
+    let available_range = TextRange::new(
+        match_expr.syntax().text_range().start(),
+        match_arm_list.syntax().text_range().start(),
+    );
+    let cursor_in_range = available_range.contains_range(ctx.selection_trimmed());
+    if !cursor_in_range {
+        return None;
+    }
 
-    let mut arms = match_expr.match_arm_list()?.arms();
+    let mut arms = match_arm_list.arms();
     let (first_arm, second_arm) = (arms.next()?, arms.next()?);
     if arms.next().is_some() || first_arm.guard().is_some() || second_arm.guard().is_some() {
         return None;
@@ -226,10 +237,20 @@ pub(crate) fn replace_match_with_if_let(acc: &mut Assists, ctx: &AssistContext<'
     )?;
     let scrutinee = match_expr.expr()?;
 
+    let let_ = match &if_let_pat {
+        ast::Pat::LiteralPat(p)
+            if p.literal()
+                .map(|it| it.token().kind())
+                .map_or(false, |it| it == T![true] || it == T![false]) =>
+        {
+            ""
+        }
+        _ => " let",
+    };
     let target = match_expr.syntax().text_range();
     acc.add(
         AssistId("replace_match_with_if_let", AssistKind::RefactorRewrite),
-        "Replace match with if let",
+        format!("Replace match with if{let_}"),
         target,
         move |edit| {
             fn make_block_expr(expr: ast::Expr) -> ast::BlockExpr {
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs
new file mode 100644
index 00000000000..a7e3ed793f1
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs
@@ -0,0 +1,310 @@
+use ide_db::assists::{AssistId, AssistKind};
+use syntax::{
+    ast::{self, make, Expr, HasArgList},
+    AstNode,
+};
+
+use crate::{AssistContext, Assists};
+
+// Assist: replace_with_lazy_method
+//
+// Replace `unwrap_or` with `unwrap_or_else` and `ok_or` with `ok_or_else`.
+//
+// ```
+// # //- minicore:option, fn
+// fn foo() {
+//     let a = Some(1);
+//     a.unwra$0p_or(2);
+// }
+// ```
+// ->
+// ```
+// fn foo() {
+//     let a = Some(1);
+//     a.unwrap_or_else(|| 2);
+// }
+// ```
+pub(crate) fn replace_with_lazy_method(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+    let call: ast::MethodCallExpr = ctx.find_node_at_offset()?;
+    let scope = ctx.sema.scope(call.syntax())?;
+
+    let last_arg = call.arg_list()?.args().next()?;
+    let method_name = call.name_ref()?;
+
+    let callable = ctx.sema.resolve_method_call_as_callable(&call)?;
+    let (_, receiver_ty) = callable.receiver_param(ctx.sema.db)?;
+    let n_params = callable.n_params() + 1;
+
+    let method_name_lazy = format!(
+        "{method_name}{}",
+        if method_name.text().ends_with("or") { "_else" } else { "_with" }
+    );
+
+    receiver_ty.iterate_method_candidates_with_traits(
+        ctx.sema.db,
+        &scope,
+        &scope.visible_traits().0,
+        None,
+        None,
+        |func| {
+            let valid = func.name(ctx.sema.db).as_str() == Some(&*method_name_lazy)
+                && func.num_params(ctx.sema.db) == n_params
+                && {
+                    let params = func.params_without_self(ctx.sema.db);
+                    let last_p = params.first()?;
+                    // FIXME: Check that this has the form of `() -> T` where T is the current type of the argument
+                    last_p.ty().impls_fnonce(ctx.sema.db)
+                };
+            valid.then_some(func)
+        },
+    )?;
+
+    acc.add(
+        AssistId("replace_with_lazy_method", AssistKind::RefactorRewrite),
+        format!("Replace {method_name} with {method_name_lazy}"),
+        call.syntax().text_range(),
+        |builder| {
+            builder.replace(method_name.syntax().text_range(), method_name_lazy);
+            let closured = into_closure(&last_arg);
+            builder.replace_ast(last_arg, closured);
+        },
+    )
+}
+
+fn into_closure(param: &Expr) -> Expr {
+    (|| {
+        if let ast::Expr::CallExpr(call) = param {
+            if call.arg_list()?.args().count() == 0 {
+                Some(call.expr()?)
+            } else {
+                None
+            }
+        } else {
+            None
+        }
+    })()
+    .unwrap_or_else(|| make::expr_closure(None, param.clone()))
+}
+
+// Assist: replace_with_eager_method
+//
+// Replace `unwrap_or_else` with `unwrap_or` and `ok_or_else` with `ok_or`.
+//
+// ```
+// # //- minicore:option, fn
+// fn foo() {
+//     let a = Some(1);
+//     a.unwra$0p_or_else(|| 2);
+// }
+// ```
+// ->
+// ```
+// fn foo() {
+//     let a = Some(1);
+//     a.unwrap_or(2);
+// }
+// ```
+pub(crate) fn replace_with_eager_method(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+    let call: ast::MethodCallExpr = ctx.find_node_at_offset()?;
+    let scope = ctx.sema.scope(call.syntax())?;
+
+    let last_arg = call.arg_list()?.args().next()?;
+    let method_name = call.name_ref()?;
+
+    let callable = ctx.sema.resolve_method_call_as_callable(&call)?;
+    let (_, receiver_ty) = callable.receiver_param(ctx.sema.db)?;
+    let n_params = callable.n_params() + 1;
+    let params = callable.params(ctx.sema.db);
+
+    // FIXME: Check that the arg is of the form `() -> T`
+    if !params.first()?.1.impls_fnonce(ctx.sema.db) {
+        return None;
+    }
+
+    let method_name_text = method_name.text();
+    let method_name_eager = method_name_text
+        .strip_suffix("_else")
+        .or_else(|| method_name_text.strip_suffix("_with"))?;
+
+    receiver_ty.iterate_method_candidates_with_traits(
+        ctx.sema.db,
+        &scope,
+        &scope.visible_traits().0,
+        None,
+        None,
+        |func| {
+            let valid = func.name(ctx.sema.db).as_str() == Some(&*method_name_eager)
+                && func.num_params(ctx.sema.db) == n_params;
+            valid.then_some(func)
+        },
+    )?;
+
+    acc.add(
+        AssistId("replace_with_eager_method", AssistKind::RefactorRewrite),
+        format!("Replace {method_name} with {method_name_eager}"),
+        call.syntax().text_range(),
+        |builder| {
+            builder.replace(method_name.syntax().text_range(), method_name_eager);
+            let called = into_call(&last_arg);
+            builder.replace_ast(last_arg, called);
+        },
+    )
+}
+
+fn into_call(param: &Expr) -> Expr {
+    (|| {
+        if let ast::Expr::ClosureExpr(closure) = param {
+            if closure.param_list()?.params().count() == 0 {
+                Some(closure.body()?)
+            } else {
+                None
+            }
+        } else {
+            None
+        }
+    })()
+    .unwrap_or_else(|| make::expr_call(param.clone(), make::arg_list(Vec::new())))
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::tests::check_assist;
+
+    use super::*;
+
+    #[test]
+    fn replace_or_with_or_else_simple() {
+        check_assist(
+            replace_with_lazy_method,
+            r#"
+//- minicore: option, fn
+fn foo() {
+    let foo = Some(1);
+    return foo.unwrap_$0or(2);
+}
+"#,
+            r#"
+fn foo() {
+    let foo = Some(1);
+    return foo.unwrap_or_else(|| 2);
+}
+"#,
+        )
+    }
+
+    #[test]
+    fn replace_or_with_or_else_call() {
+        check_assist(
+            replace_with_lazy_method,
+            r#"
+//- minicore: option, fn
+fn foo() {
+    let foo = Some(1);
+    return foo.unwrap_$0or(x());
+}
+"#,
+            r#"
+fn foo() {
+    let foo = Some(1);
+    return foo.unwrap_or_else(x);
+}
+"#,
+        )
+    }
+
+    #[test]
+    fn replace_or_with_or_else_block() {
+        check_assist(
+            replace_with_lazy_method,
+            r#"
+//- minicore: option, fn
+fn foo() {
+    let foo = Some(1);
+    return foo.unwrap_$0or({
+        let mut x = bar();
+        for i in 0..10 {
+            x += i;
+        }
+        x
+    });
+}
+"#,
+            r#"
+fn foo() {
+    let foo = Some(1);
+    return foo.unwrap_or_else(|| {
+        let mut x = bar();
+        for i in 0..10 {
+            x += i;
+        }
+        x
+    });
+}
+"#,
+        )
+    }
+
+    #[test]
+    fn replace_or_else_with_or_simple() {
+        check_assist(
+            replace_with_eager_method,
+            r#"
+//- minicore: option, fn
+fn foo() {
+    let foo = Some(1);
+    return foo.unwrap_$0or_else(|| 2);
+}
+"#,
+            r#"
+fn foo() {
+    let foo = Some(1);
+    return foo.unwrap_or(2);
+}
+"#,
+        )
+    }
+
+    #[test]
+    fn replace_or_else_with_or_call() {
+        check_assist(
+            replace_with_eager_method,
+            r#"
+//- minicore: option, fn
+fn foo() {
+    let foo = Some(1);
+    return foo.unwrap_$0or_else(x);
+}
+
+fn x() -> i32 { 0 }
+"#,
+            r#"
+fn foo() {
+    let foo = Some(1);
+    return foo.unwrap_or(x());
+}
+
+fn x() -> i32 { 0 }
+"#,
+        )
+    }
+
+    #[test]
+    fn replace_or_else_with_or_map() {
+        check_assist(
+            replace_with_eager_method,
+            r#"
+//- minicore: option, fn
+fn foo() {
+    let foo = Some("foo");
+    return foo.map$0_or_else(|| 42, |v| v.len());
+}
+"#,
+            r#"
+fn foo() {
+    let foo = Some("foo");
+    return foo.map_or(42, |v| v.len());
+}
+"#,
+        )
+    }
+}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_or_with_or_else.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_or_with_or_else.rs
deleted file mode 100644
index f0ed3c4fe6f..00000000000
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_or_with_or_else.rs
+++ /dev/null
@@ -1,364 +0,0 @@
-use ide_db::{
-    assists::{AssistId, AssistKind},
-    famous_defs::FamousDefs,
-};
-use syntax::{
-    ast::{self, make, Expr, HasArgList},
-    AstNode,
-};
-
-use crate::{AssistContext, Assists};
-
-// Assist: replace_or_with_or_else
-//
-// Replace `unwrap_or` with `unwrap_or_else` and `ok_or` with `ok_or_else`.
-//
-// ```
-// # //- minicore:option
-// fn foo() {
-//     let a = Some(1);
-//     a.unwra$0p_or(2);
-// }
-// ```
-// ->
-// ```
-// fn foo() {
-//     let a = Some(1);
-//     a.unwrap_or_else(|| 2);
-// }
-// ```
-pub(crate) fn replace_or_with_or_else(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
-    let call: ast::MethodCallExpr = ctx.find_node_at_offset()?;
-
-    let kind = is_option_or_result(call.receiver()?, ctx)?;
-
-    let (name, arg_list) = (call.name_ref()?, call.arg_list()?);
-
-    let mut map_or = false;
-
-    let replace = match &*name.text() {
-        "unwrap_or" => "unwrap_or_else".to_string(),
-        "or" => "or_else".to_string(),
-        "ok_or" if kind == Kind::Option => "ok_or_else".to_string(),
-        "map_or" => {
-            map_or = true;
-            "map_or_else".to_string()
-        }
-        _ => return None,
-    };
-
-    let arg = match arg_list.args().collect::<Vec<_>>().as_slice() {
-        [] => make::arg_list(Vec::new()),
-        [first] => {
-            let param = into_closure(first);
-            make::arg_list(vec![param])
-        }
-        [first, second] if map_or => {
-            let param = into_closure(first);
-            make::arg_list(vec![param, second.clone()])
-        }
-        _ => return None,
-    };
-
-    acc.add(
-        AssistId("replace_or_with_or_else", AssistKind::RefactorRewrite),
-        format!("Replace {name} with {replace}"),
-        call.syntax().text_range(),
-        |builder| {
-            builder.replace(name.syntax().text_range(), replace);
-            builder.replace_ast(arg_list, arg)
-        },
-    )
-}
-
-fn into_closure(param: &Expr) -> Expr {
-    (|| {
-        if let ast::Expr::CallExpr(call) = param {
-            if call.arg_list()?.args().count() == 0 {
-                Some(call.expr()?)
-            } else {
-                None
-            }
-        } else {
-            None
-        }
-    })()
-    .unwrap_or_else(|| make::expr_closure(None, param.clone()))
-}
-
-// Assist: replace_or_else_with_or
-//
-// Replace `unwrap_or_else` with `unwrap_or` and `ok_or_else` with `ok_or`.
-//
-// ```
-// # //- minicore:option
-// fn foo() {
-//     let a = Some(1);
-//     a.unwra$0p_or_else(|| 2);
-// }
-// ```
-// ->
-// ```
-// fn foo() {
-//     let a = Some(1);
-//     a.unwrap_or(2);
-// }
-// ```
-pub(crate) fn replace_or_else_with_or(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
-    let call: ast::MethodCallExpr = ctx.find_node_at_offset()?;
-
-    let kind = is_option_or_result(call.receiver()?, ctx)?;
-
-    let (name, arg_list) = (call.name_ref()?, call.arg_list()?);
-
-    let mut map_or = false;
-    let replace = match &*name.text() {
-        "unwrap_or_else" => "unwrap_or".to_string(),
-        "or_else" => "or".to_string(),
-        "ok_or_else" if kind == Kind::Option => "ok_or".to_string(),
-        "map_or_else" => {
-            map_or = true;
-            "map_or".to_string()
-        }
-        _ => return None,
-    };
-
-    let arg = match arg_list.args().collect::<Vec<_>>().as_slice() {
-        [] => make::arg_list(Vec::new()),
-        [first] => {
-            let param = into_call(first);
-            make::arg_list(vec![param])
-        }
-        [first, second] if map_or => {
-            let param = into_call(first);
-            make::arg_list(vec![param, second.clone()])
-        }
-        _ => return None,
-    };
-
-    acc.add(
-        AssistId("replace_or_else_with_or", AssistKind::RefactorRewrite),
-        format!("Replace {name} with {replace}"),
-        call.syntax().text_range(),
-        |builder| {
-            builder.replace(name.syntax().text_range(), replace);
-            builder.replace_ast(arg_list, arg)
-        },
-    )
-}
-
-fn into_call(param: &Expr) -> Expr {
-    (|| {
-        if let ast::Expr::ClosureExpr(closure) = param {
-            if closure.param_list()?.params().count() == 0 {
-                Some(closure.body()?)
-            } else {
-                None
-            }
-        } else {
-            None
-        }
-    })()
-    .unwrap_or_else(|| make::expr_call(param.clone(), make::arg_list(Vec::new())))
-}
-
-#[derive(PartialEq, Eq)]
-enum Kind {
-    Option,
-    Result,
-}
-
-fn is_option_or_result(receiver: Expr, ctx: &AssistContext<'_>) -> Option<Kind> {
-    let ty = ctx.sema.type_of_expr(&receiver)?.adjusted().as_adt()?.as_enum()?;
-    let option_enum =
-        FamousDefs(&ctx.sema, ctx.sema.scope(receiver.syntax())?.krate()).core_option_Option();
-
-    if let Some(option_enum) = option_enum {
-        if ty == option_enum {
-            return Some(Kind::Option);
-        }
-    }
-
-    let result_enum =
-        FamousDefs(&ctx.sema, ctx.sema.scope(receiver.syntax())?.krate()).core_result_Result();
-
-    if let Some(result_enum) = result_enum {
-        if ty == result_enum {
-            return Some(Kind::Result);
-        }
-    }
-
-    None
-}
-
-#[cfg(test)]
-mod tests {
-    use crate::tests::{check_assist, check_assist_not_applicable};
-
-    use super::*;
-
-    #[test]
-    fn replace_or_with_or_else_simple() {
-        check_assist(
-            replace_or_with_or_else,
-            r#"
-//- minicore: option
-fn foo() {
-    let foo = Some(1);
-    return foo.unwrap_$0or(2);
-}
-"#,
-            r#"
-fn foo() {
-    let foo = Some(1);
-    return foo.unwrap_or_else(|| 2);
-}
-"#,
-        )
-    }
-
-    #[test]
-    fn replace_or_with_or_else_call() {
-        check_assist(
-            replace_or_with_or_else,
-            r#"
-//- minicore: option
-fn foo() {
-    let foo = Some(1);
-    return foo.unwrap_$0or(x());
-}
-"#,
-            r#"
-fn foo() {
-    let foo = Some(1);
-    return foo.unwrap_or_else(x);
-}
-"#,
-        )
-    }
-
-    #[test]
-    fn replace_or_with_or_else_block() {
-        check_assist(
-            replace_or_with_or_else,
-            r#"
-//- minicore: option
-fn foo() {
-    let foo = Some(1);
-    return foo.unwrap_$0or({
-        let mut x = bar();
-        for i in 0..10 {
-            x += i;
-        }
-        x
-    });
-}
-"#,
-            r#"
-fn foo() {
-    let foo = Some(1);
-    return foo.unwrap_or_else(|| {
-        let mut x = bar();
-        for i in 0..10 {
-            x += i;
-        }
-        x
-    });
-}
-"#,
-        )
-    }
-
-    #[test]
-    fn replace_or_else_with_or_simple() {
-        check_assist(
-            replace_or_else_with_or,
-            r#"
-//- minicore: option
-fn foo() {
-    let foo = Some(1);
-    return foo.unwrap_$0or_else(|| 2);
-}
-"#,
-            r#"
-fn foo() {
-    let foo = Some(1);
-    return foo.unwrap_or(2);
-}
-"#,
-        )
-    }
-
-    #[test]
-    fn replace_or_else_with_or_call() {
-        check_assist(
-            replace_or_else_with_or,
-            r#"
-//- minicore: option
-fn foo() {
-    let foo = Some(1);
-    return foo.unwrap_$0or_else(x);
-}
-"#,
-            r#"
-fn foo() {
-    let foo = Some(1);
-    return foo.unwrap_or(x());
-}
-"#,
-        )
-    }
-
-    #[test]
-    fn replace_or_else_with_or_result() {
-        check_assist(
-            replace_or_else_with_or,
-            r#"
-//- minicore: result
-fn foo() {
-    let foo = Ok(1);
-    return foo.unwrap_$0or_else(x);
-}
-"#,
-            r#"
-fn foo() {
-    let foo = Ok(1);
-    return foo.unwrap_or(x());
-}
-"#,
-        )
-    }
-
-    #[test]
-    fn replace_or_else_with_or_map() {
-        check_assist(
-            replace_or_else_with_or,
-            r#"
-//- minicore: result
-fn foo() {
-    let foo = Ok("foo");
-    return foo.map$0_or_else(|| 42, |v| v.len());
-}
-"#,
-            r#"
-fn foo() {
-    let foo = Ok("foo");
-    return foo.map_or(42, |v| v.len());
-}
-"#,
-        )
-    }
-
-    #[test]
-    fn replace_or_else_with_or_not_applicable() {
-        check_assist_not_applicable(
-            replace_or_else_with_or,
-            r#"
-fn foo() {
-    let foo = Ok(1);
-    return foo.unwrap_$0or_else(x);
-}
-"#,
-        )
-    }
-}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs
index 276cf5f5dd0..4d489b62b5c 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs
@@ -188,7 +188,7 @@ mod handlers {
     mod replace_try_expr_with_match;
     mod replace_derive_with_manual_impl;
     mod replace_if_let_with_match;
-    mod replace_or_with_or_else;
+    mod replace_method_eager_lazy;
     mod replace_arith_op;
     mod introduce_named_generic;
     mod replace_let_with_if_let;
@@ -297,8 +297,8 @@ mod handlers {
             replace_if_let_with_match::replace_if_let_with_match,
             replace_if_let_with_match::replace_match_with_if_let,
             replace_let_with_if_let::replace_let_with_if_let,
-            replace_or_with_or_else::replace_or_else_with_or,
-            replace_or_with_or_else::replace_or_with_or_else,
+            replace_method_eager_lazy::replace_with_eager_method,
+            replace_method_eager_lazy::replace_with_lazy_method,
             replace_turbofish_with_explicit_type::replace_turbofish_with_explicit_type,
             replace_qualified_name_with_use::replace_qualified_name_with_use,
             replace_arith_op::replace_arith_with_wrapping,
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs
index 8a25e1f648a..e5a8d675a9e 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs
@@ -2006,12 +2006,12 @@ fn doctest_remove_dbg() {
         "remove_dbg",
         r#####"
 fn main() {
-    $0dbg!(92);
+    let x = $0dbg!(42 * dbg!(4 + 2));$0
 }
 "#####,
         r#####"
 fn main() {
-    92;
+    let x = 42 * (4 + 2);
 }
 "#####,
     )
@@ -2314,46 +2314,6 @@ fn handle(action: Action) {
 }
 
 #[test]
-fn doctest_replace_or_else_with_or() {
-    check_doc_test(
-        "replace_or_else_with_or",
-        r#####"
-//- minicore:option
-fn foo() {
-    let a = Some(1);
-    a.unwra$0p_or_else(|| 2);
-}
-"#####,
-        r#####"
-fn foo() {
-    let a = Some(1);
-    a.unwrap_or(2);
-}
-"#####,
-    )
-}
-
-#[test]
-fn doctest_replace_or_with_or_else() {
-    check_doc_test(
-        "replace_or_with_or_else",
-        r#####"
-//- minicore:option
-fn foo() {
-    let a = Some(1);
-    a.unwra$0p_or(2);
-}
-"#####,
-        r#####"
-fn foo() {
-    let a = Some(1);
-    a.unwrap_or_else(|| 2);
-}
-"#####,
-    )
-}
-
-#[test]
 fn doctest_replace_qualified_name_with_use() {
     check_doc_test(
         "replace_qualified_name_with_use",
@@ -2428,6 +2388,46 @@ fn main() {
 }
 
 #[test]
+fn doctest_replace_with_eager_method() {
+    check_doc_test(
+        "replace_with_eager_method",
+        r#####"
+//- minicore:option, fn
+fn foo() {
+    let a = Some(1);
+    a.unwra$0p_or_else(|| 2);
+}
+"#####,
+        r#####"
+fn foo() {
+    let a = Some(1);
+    a.unwrap_or(2);
+}
+"#####,
+    )
+}
+
+#[test]
+fn doctest_replace_with_lazy_method() {
+    check_doc_test(
+        "replace_with_lazy_method",
+        r#####"
+//- minicore:option, fn
+fn foo() {
+    let a = Some(1);
+    a.unwra$0p_or(2);
+}
+"#####,
+        r#####"
+fn foo() {
+    let a = Some(1);
+    a.unwrap_or_else(|| 2);
+}
+"#####,
+    )
+}
+
+#[test]
 fn doctest_sort_items() {
     check_doc_test(
         "sort_items",
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs
index 7c6e5e100f6..09ac57153ae 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs
@@ -122,7 +122,7 @@ fn complete_methods(
     mut f: impl FnMut(hir::Function),
 ) {
     let mut seen_methods = FxHashSet::default();
-    receiver.iterate_method_candidates(
+    receiver.iterate_method_candidates_with_traits(
         ctx.db,
         &ctx.scope,
         &ctx.traits_in_scope(),
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs
index 364969af9c9..0979f6a6dfc 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs
@@ -5,10 +5,7 @@ use ide_db::imports::{
     insert_use::ImportScope,
 };
 use itertools::Itertools;
-use syntax::{
-    ast::{self},
-    AstNode, SyntaxNode, T,
-};
+use syntax::{ast, AstNode, SyntaxNode, T};
 
 use crate::{
     context::{
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs
index 37849c251a4..69c05a76df4 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs
@@ -33,7 +33,9 @@ pub(crate) fn complete_type_path(
             // Don't suggest attribute macros and derives.
             ScopeDef::ModuleDef(Macro(mac)) => mac.is_fn_like(ctx.db),
             // Type things are fine
-            ScopeDef::ModuleDef(BuiltinType(_) | Adt(_) | Module(_) | Trait(_) | TypeAlias(_))
+            ScopeDef::ModuleDef(
+                BuiltinType(_) | Adt(_) | Module(_) | Trait(_) | TraitAlias(_) | TypeAlias(_),
+            )
             | ScopeDef::AdtSelfType(_)
             | ScopeDef::Unknown
             | ScopeDef::GenericParam(TypeParam(_)) => true,
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs
index ea54068b0f8..7dc29c3d5ac 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs
@@ -6,13 +6,13 @@ mod tests;
 
 use std::iter;
 
-use base_db::SourceDatabaseExt;
 use hir::{
     HasAttrs, Local, Name, PathResolution, ScopeDef, Semantics, SemanticsScope, Type, TypeInfo,
 };
 use ide_db::{
     base_db::{FilePosition, SourceDatabase},
     famous_defs::FamousDefs,
+    helpers::is_editable_crate,
     FxHashMap, FxHashSet, RootDatabase,
 };
 use syntax::{
@@ -387,8 +387,7 @@ pub(crate) struct CompletionContext<'a> {
 impl<'a> CompletionContext<'a> {
     /// The range of the identifier that is being completed.
     pub(crate) fn source_range(&self) -> TextRange {
-        // check kind of macro-expanded token, but use range of original token
-        let kind = self.token.kind();
+        let kind = self.original_token.kind();
         match kind {
             CHAR => {
                 // assume we are completing a lifetime but the user has only typed the '
@@ -416,6 +415,7 @@ impl<'a> CompletionContext<'a> {
                 hir::ModuleDef::Const(it) => self.is_visible(it),
                 hir::ModuleDef::Static(it) => self.is_visible(it),
                 hir::ModuleDef::Trait(it) => self.is_visible(it),
+                hir::ModuleDef::TraitAlias(it) => self.is_visible(it),
                 hir::ModuleDef::TypeAlias(it) => self.is_visible(it),
                 hir::ModuleDef::Macro(it) => self.is_visible(it),
                 hir::ModuleDef::BuiltinType(_) => Visible::Yes,
@@ -525,10 +525,11 @@ impl<'a> CompletionContext<'a> {
                 return Visible::No;
             }
             // If the definition location is editable, also show private items
-            let root_file = defining_crate.root_file(self.db);
-            let source_root_id = self.db.file_source_root(root_file);
-            let is_editable = !self.db.source_root(source_root_id).is_library;
-            return if is_editable { Visible::Editable } else { Visible::No };
+            return if is_editable_crate(defining_crate, self.db) {
+                Visible::Editable
+            } else {
+                Visible::No
+            };
         }
 
         if self.is_doc_hidden(attrs, defining_crate) {
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs
index 2f65491d85e..bb9fa7ccacc 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs
@@ -288,7 +288,7 @@ impl_from!(SymbolKind for CompletionItemKind);
 
 impl CompletionItemKind {
     #[cfg(test)]
-    pub(crate) fn tag(&self) -> &'static str {
+    pub(crate) fn tag(self) -> &'static str {
         match self {
             CompletionItemKind::SymbolKind(kind) => match kind {
                 SymbolKind::Attribute => "at",
@@ -312,6 +312,7 @@ impl CompletionItemKind {
                 SymbolKind::Struct => "st",
                 SymbolKind::ToolModule => "tm",
                 SymbolKind::Trait => "tt",
+                SymbolKind::TraitAlias => "tr",
                 SymbolKind::TypeAlias => "ta",
                 SymbolKind::TypeParam => "tp",
                 SymbolKind::Union => "un",
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs
index d99ad5f9f04..c1f51aabb96 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs
@@ -367,6 +367,9 @@ fn res_to_kind(resolution: ScopeDef) -> CompletionItemKind {
         ScopeDef::ModuleDef(Const(..)) => CompletionItemKind::SymbolKind(SymbolKind::Const),
         ScopeDef::ModuleDef(Static(..)) => CompletionItemKind::SymbolKind(SymbolKind::Static),
         ScopeDef::ModuleDef(Trait(..)) => CompletionItemKind::SymbolKind(SymbolKind::Trait),
+        ScopeDef::ModuleDef(TraitAlias(..)) => {
+            CompletionItemKind::SymbolKind(SymbolKind::TraitAlias)
+        }
         ScopeDef::ModuleDef(TypeAlias(..)) => CompletionItemKind::SymbolKind(SymbolKind::TypeAlias),
         ScopeDef::ModuleDef(BuiltinType(..)) => CompletionItemKind::BuiltinType,
         ScopeDef::GenericParam(param) => CompletionItemKind::SymbolKind(match param {
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs
index ffcad1185aa..44e88607633 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs
@@ -267,4 +267,63 @@ fn main() {
 "#,
         );
     }
+
+    #[test]
+    fn complete_missing_macro_arg() {
+        // Regression test for https://github.com/rust-lang/rust-analyzer/issues/14246
+        check_edit(
+            "BAR",
+            r#"
+macro_rules! foo {
+    ($val:ident,  $val2: ident) => {
+        $val $val2
+    };
+}
+
+const BAR: u32 = 9;
+fn main() {
+    foo!(BAR, $0)
+}
+"#,
+            r#"
+macro_rules! foo {
+    ($val:ident,  $val2: ident) => {
+        $val $val2
+    };
+}
+
+const BAR: u32 = 9;
+fn main() {
+    foo!(BAR, BAR)
+}
+"#,
+        );
+        check_edit(
+            "BAR",
+            r#"
+macro_rules! foo {
+    ($val:ident,  $val2: ident) => {
+        $val $val2
+    };
+}
+
+const BAR: u32 = 9;
+fn main() {
+    foo!($0)
+}
+"#,
+            r#"
+macro_rules! foo {
+    ($val:ident,  $val2: ident) => {
+        $val $val2
+    };
+}
+
+const BAR: u32 = 9;
+fn main() {
+    foo!(BAR)
+}
+"#,
+        );
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs
index 4e60820dd6d..c97144b61b6 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs
@@ -857,9 +857,9 @@ mod lint {
     #[test]
     fn lint_feature() {
         check_edit(
-            "box_syntax",
+            "box_patterns",
             r#"#[feature(box_$0)] struct Test;"#,
-            r#"#[feature(box_syntax)] struct Test;"#,
+            r#"#[feature(box_patterns)] struct Test;"#,
         )
     }
 
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/active_parameter.rs b/src/tools/rust-analyzer/crates/ide-db/src/active_parameter.rs
index 2b6b60547b3..0da4e729a8d 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/active_parameter.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/active_parameter.rs
@@ -96,6 +96,7 @@ pub fn generic_def_for_node(
                     hir::PathResolution::Def(hir::ModuleDef::Adt(it)) => it.into(),
                     hir::PathResolution::Def(hir::ModuleDef::Function(it)) => it.into(),
                     hir::PathResolution::Def(hir::ModuleDef::Trait(it)) => it.into(),
+                    hir::PathResolution::Def(hir::ModuleDef::TraitAlias(it)) => it.into(),
                     hir::PathResolution::Def(hir::ModuleDef::TypeAlias(it)) => it.into(),
                     hir::PathResolution::Def(hir::ModuleDef::Variant(it)) => it.into(),
                     hir::PathResolution::Def(hir::ModuleDef::BuiltinType(_))
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs b/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs
index b1ee9b58d5b..244e99fe2e2 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs
@@ -73,68 +73,96 @@ impl RootDatabase {
 
             // AstDatabase
             hir::db::AstIdMapQuery
+            hir::db::ParseMacroExpansionQuery
+            hir::db::InternMacroCallQuery
             hir::db::MacroArgTextQuery
             hir::db::MacroDefQuery
-            hir::db::ParseMacroExpansionQuery
             hir::db::MacroExpandQuery
+            hir::db::ExpandProcMacroQuery
+            hir::db::MacroExpandErrorQuery
             hir::db::HygieneFrameQuery
-            hir::db::InternMacroCallQuery
 
             // DefDatabase
             hir::db::FileItemTreeQuery
-            hir::db::BlockDefMapQuery
             hir::db::CrateDefMapQueryQuery
-            hir::db::FieldsAttrsQuery
-            hir::db::VariantsAttrsQuery
-            hir::db::FieldsAttrsSourceMapQuery
-            hir::db::VariantsAttrsSourceMapQuery
+            hir::db::BlockDefMapQuery
             hir::db::StructDataQuery
+            hir::db::StructDataWithDiagnosticsQuery
             hir::db::UnionDataQuery
+            hir::db::UnionDataWithDiagnosticsQuery
             hir::db::EnumDataQuery
+            hir::db::EnumDataWithDiagnosticsQuery
             hir::db::ImplDataQuery
+            hir::db::ImplDataWithDiagnosticsQuery
             hir::db::TraitDataQuery
+            hir::db::TraitDataWithDiagnosticsQuery
+            hir::db::TraitAliasDataQuery
             hir::db::TypeAliasDataQuery
             hir::db::FunctionDataQuery
             hir::db::ConstDataQuery
             hir::db::StaticDataQuery
+            hir::db::Macro2DataQuery
+            hir::db::MacroRulesDataQuery
+            hir::db::ProcMacroDataQuery
             hir::db::BodyWithSourceMapQuery
             hir::db::BodyQuery
             hir::db::ExprScopesQuery
             hir::db::GenericParamsQuery
+            hir::db::VariantsAttrsQuery
+            hir::db::FieldsAttrsQuery
+            hir::db::VariantsAttrsSourceMapQuery
+            hir::db::FieldsAttrsSourceMapQuery
             hir::db::AttrsQuery
             hir::db::CrateLangItemsQuery
             hir::db::LangItemQuery
             hir::db::ImportMapQuery
+            hir::db::FieldVisibilitiesQuery
+            hir::db::FunctionVisibilityQuery
+            hir::db::ConstVisibilityQuery
+            hir::db::CrateSupportsNoStdQuery
 
             // HirDatabase
             hir::db::InferQueryQuery
+            hir::db::MirBodyQuery
+            hir::db::BorrowckQuery
             hir::db::TyQuery
             hir::db::ValueTyQuery
             hir::db::ImplSelfTyQuery
+            hir::db::ConstParamTyQuery
+            hir::db::ConstEvalQuery
+            hir::db::ConstEvalDiscriminantQuery
             hir::db::ImplTraitQuery
             hir::db::FieldTypesQuery
+            hir::db::LayoutOfAdtQuery
+            hir::db::TargetDataLayoutQuery
             hir::db::CallableItemSignatureQuery
+            hir::db::ReturnTypeImplTraitsQuery
             hir::db::GenericPredicatesForParamQuery
             hir::db::GenericPredicatesQuery
+            hir::db::TraitEnvironmentQuery
             hir::db::GenericDefaultsQuery
             hir::db::InherentImplsInCrateQuery
-            hir::db::TraitEnvironmentQuery
+            hir::db::InherentImplsInBlockQuery
+            hir::db::IncoherentInherentImplCratesQuery
             hir::db::TraitImplsInCrateQuery
+            hir::db::TraitImplsInBlockQuery
             hir::db::TraitImplsInDepsQuery
-            hir::db::AssociatedTyDataQuery
+            hir::db::InternCallableDefQuery
+            hir::db::InternLifetimeParamIdQuery
+            hir::db::InternImplTraitIdQuery
+            hir::db::InternTypeOrConstParamIdQuery
+            hir::db::InternClosureQuery
+            hir::db::InternGeneratorQuery
             hir::db::AssociatedTyDataQuery
             hir::db::TraitDatumQuery
             hir::db::StructDatumQuery
             hir::db::ImplDatumQuery
             hir::db::FnDefDatumQuery
-            hir::db::ReturnTypeImplTraitsQuery
-            hir::db::InternCallableDefQuery
-            hir::db::InternTypeOrConstParamIdQuery
-            hir::db::InternImplTraitIdQuery
-            hir::db::InternClosureQuery
+            hir::db::FnDefVarianceQuery
+            hir::db::AdtVarianceQuery
             hir::db::AssociatedTyValueQuery
             hir::db::TraitSolveQueryQuery
-            hir::db::InternTypeOrConstParamIdQuery
+            hir::db::ProgramClausesForChalkEnvQuery
 
             // SymbolsDatabase
             crate::symbol_index::ModuleSymbolsQuery
@@ -153,8 +181,14 @@ impl RootDatabase {
             hir::db::InternConstQuery
             hir::db::InternStaticQuery
             hir::db::InternTraitQuery
+            hir::db::InternTraitAliasQuery
             hir::db::InternTypeAliasQuery
             hir::db::InternImplQuery
+            hir::db::InternExternBlockQuery
+            hir::db::InternBlockQuery
+            hir::db::InternMacro2Query
+            hir::db::InternProcMacroQuery
+            hir::db::InternMacroRulesQuery
         ];
 
         acc.sort_by_key(|it| std::cmp::Reverse(it.1));
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs
index ed7f04fd8e7..1322f5228e8 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs
@@ -9,7 +9,8 @@ use arrayvec::ArrayVec;
 use hir::{
     Adt, AsAssocItem, AssocItem, BuiltinAttr, BuiltinType, Const, Crate, DeriveHelper, Field,
     Function, GenericParam, HasVisibility, Impl, ItemInNs, Label, Local, Macro, Module, ModuleDef,
-    Name, PathResolution, Semantics, Static, ToolModule, Trait, TypeAlias, Variant, Visibility,
+    Name, PathResolution, Semantics, Static, ToolModule, Trait, TraitAlias, TypeAlias, Variant,
+    Visibility,
 };
 use stdx::impl_from;
 use syntax::{
@@ -31,6 +32,7 @@ pub enum Definition {
     Const(Const),
     Static(Static),
     Trait(Trait),
+    TraitAlias(TraitAlias),
     TypeAlias(TypeAlias),
     BuiltinType(BuiltinType),
     SelfType(Impl),
@@ -64,6 +66,7 @@ impl Definition {
             Definition::Const(it) => it.module(db),
             Definition::Static(it) => it.module(db),
             Definition::Trait(it) => it.module(db),
+            Definition::TraitAlias(it) => it.module(db),
             Definition::TypeAlias(it) => it.module(db),
             Definition::Variant(it) => it.module(db),
             Definition::SelfType(it) => it.module(db),
@@ -87,6 +90,7 @@ impl Definition {
             Definition::Const(it) => it.visibility(db),
             Definition::Static(it) => it.visibility(db),
             Definition::Trait(it) => it.visibility(db),
+            Definition::TraitAlias(it) => it.visibility(db),
             Definition::TypeAlias(it) => it.visibility(db),
             Definition::Variant(it) => it.visibility(db),
             Definition::BuiltinType(_) => Visibility::Public,
@@ -113,6 +117,7 @@ impl Definition {
             Definition::Const(it) => it.name(db)?,
             Definition::Static(it) => it.name(db),
             Definition::Trait(it) => it.name(db),
+            Definition::TraitAlias(it) => it.name(db),
             Definition::TypeAlias(it) => it.name(db),
             Definition::BuiltinType(it) => it.name(),
             Definition::SelfType(_) => return None,
@@ -300,6 +305,7 @@ impl NameClass {
                 ast::Item::Module(it) => Definition::Module(sema.to_def(&it)?),
                 ast::Item::Static(it) => Definition::Static(sema.to_def(&it)?),
                 ast::Item::Trait(it) => Definition::Trait(sema.to_def(&it)?),
+                ast::Item::TraitAlias(it) => Definition::TraitAlias(sema.to_def(&it)?),
                 ast::Item::TypeAlias(it) => Definition::TypeAlias(sema.to_def(&it)?),
                 ast::Item::Enum(it) => Definition::Adt(hir::Adt::Enum(sema.to_def(&it)?)),
                 ast::Item::Struct(it) => Definition::Adt(hir::Adt::Struct(sema.to_def(&it)?)),
@@ -463,9 +469,12 @@ impl NameRefClass {
         match_ast! {
             match parent {
                 ast::MethodCallExpr(method_call) => {
-                    sema.resolve_method_call(&method_call)
-                        .map(Definition::Function)
-                        .map(NameRefClass::Definition)
+                    sema.resolve_method_call_field_fallback(&method_call)
+                        .map(|it| {
+                            it.map_left(Definition::Function)
+                                .map_right(Definition::Field)
+                                .either(NameRefClass::Definition, NameRefClass::Definition)
+                        })
                 },
                 ast::FieldExpr(field_expr) => {
                     sema.resolve_field(&field_expr)
@@ -542,7 +551,7 @@ impl NameRefClass {
 }
 
 impl_from!(
-    Field, Module, Function, Adt, Variant, Const, Static, Trait, TypeAlias, BuiltinType, Local,
+    Field, Module, Function, Adt, Variant, Const, Static, Trait, TraitAlias, TypeAlias, BuiltinType, Local,
     GenericParam, Label, Macro
     for Definition
 );
@@ -599,6 +608,7 @@ impl From<ModuleDef> for Definition {
             ModuleDef::Const(it) => Definition::Const(it),
             ModuleDef::Static(it) => Definition::Static(it),
             ModuleDef::Trait(it) => Definition::Trait(it),
+            ModuleDef::TraitAlias(it) => Definition::TraitAlias(it),
             ModuleDef::TypeAlias(it) => Definition::TypeAlias(it),
             ModuleDef::Macro(it) => Definition::Macro(it),
             ModuleDef::BuiltinType(it) => Definition::BuiltinType(it),
@@ -616,6 +626,7 @@ impl From<Definition> for Option<ItemInNs> {
             Definition::Const(it) => ModuleDef::Const(it),
             Definition::Static(it) => ModuleDef::Static(it),
             Definition::Trait(it) => ModuleDef::Trait(it),
+            Definition::TraitAlias(it) => ModuleDef::TraitAlias(it),
             Definition::TypeAlias(it) => ModuleDef::TypeAlias(it),
             Definition::BuiltinType(it) => ModuleDef::BuiltinType(it),
             _ => return None,
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/helpers.rs b/src/tools/rust-analyzer/crates/ide-db/src/helpers.rs
index 6e56efe344d..8e3b1eef15b 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/helpers.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/helpers.rs
@@ -2,8 +2,8 @@
 
 use std::collections::VecDeque;
 
-use base_db::FileId;
-use hir::{ItemInNs, ModuleDef, Name, Semantics};
+use base_db::{FileId, SourceDatabaseExt};
+use hir::{Crate, ItemInNs, ModuleDef, Name, Semantics};
 use syntax::{
     ast::{self, make},
     AstToken, SyntaxKind, SyntaxToken, TokenAtOffset,
@@ -103,3 +103,9 @@ pub fn lint_eq_or_in_group(lint: &str, lint_is: &str) -> bool {
         false
     }
 }
+
+pub fn is_editable_crate(krate: Crate, db: &RootDatabase) -> bool {
+    let root_file = krate.root_file(db);
+    let source_root_id = db.file_source_root(root_file);
+    !db.source_root(source_root_id).is_library
+}
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs
index 994d48385a0..b26b0a9087e 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs
@@ -528,7 +528,7 @@ fn trait_applicable_items(
             },
         )
     } else {
-        trait_candidate.receiver_ty.iterate_method_candidates(
+        trait_candidate.receiver_ty.iterate_method_candidates_with_traits(
             db,
             scope,
             &trait_candidates,
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs
index 156bbb634e4..ae120470047 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs
@@ -22,7 +22,7 @@ pub mod source_change;
 pub mod symbol_index;
 pub mod traits;
 pub mod ty_filter;
-pub mod use_trivial_contructor;
+pub mod use_trivial_constructor;
 
 pub mod imports {
     pub mod import_assets;
@@ -191,6 +191,7 @@ pub enum SymbolKind {
     Struct,
     ToolModule,
     Trait,
+    TraitAlias,
     TypeAlias,
     TypeParam,
     Union,
@@ -221,6 +222,7 @@ impl From<FileSymbolKind> for SymbolKind {
             FileSymbolKind::Static => SymbolKind::Static,
             FileSymbolKind::Struct => SymbolKind::Struct,
             FileSymbolKind::Trait => SymbolKind::Trait,
+            FileSymbolKind::TraitAlias => SymbolKind::TraitAlias,
             FileSymbolKind::TypeAlias => SymbolKind::TypeAlias,
             FileSymbolKind::Union => SymbolKind::Union,
         }
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/rename.rs b/src/tools/rust-analyzer/crates/ide-db/src/rename.rs
index 84d70b258ff..f710211c8cb 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/rename.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/rename.rs
@@ -119,15 +119,9 @@ impl Definition {
             Definition::Const(it) => name_range(it, sema),
             Definition::Static(it) => name_range(it, sema),
             Definition::Trait(it) => name_range(it, sema),
+            Definition::TraitAlias(it) => name_range(it, sema),
             Definition::TypeAlias(it) => name_range(it, sema),
-            Definition::Local(local) => {
-                let src = local.source(sema.db);
-                let name = match &src.value {
-                    Either::Left(bind_pat) => bind_pat.name()?,
-                    Either::Right(_) => return None,
-                };
-                src.with_value(name.syntax()).original_file_range_opt(sema.db)
-            }
+            Definition::Local(it) => name_range(it.primary_source(sema.db), sema),
             Definition::GenericParam(generic_param) => match generic_param {
                 hir::GenericParam::LifetimeParam(lifetime_param) => {
                     let src = lifetime_param.source(sema.db)?;
@@ -301,13 +295,7 @@ fn rename_reference(
         source_change.insert_source_edit(file_id, edit);
         Ok(())
     };
-    match def {
-        Definition::Local(l) => l
-            .associated_locals(sema.db)
-            .iter()
-            .try_for_each(|&local| insert_def_edit(Definition::Local(local))),
-        def => insert_def_edit(def),
-    }?;
+    insert_def_edit(def)?;
     Ok(source_change)
 }
 
@@ -470,59 +458,64 @@ fn source_edit_from_def(
     def: Definition,
     new_name: &str,
 ) -> Result<(FileId, TextEdit)> {
-    let FileRange { file_id, range } = def
-        .range_for_rename(sema)
-        .ok_or_else(|| format_err!("No identifier available to rename"))?;
-
     let mut edit = TextEdit::builder();
     if let Definition::Local(local) = def {
-        if let Either::Left(pat) = local.source(sema.db).value {
-            // special cases required for renaming fields/locals in Record patterns
-            if let Some(pat_field) = pat.syntax().parent().and_then(ast::RecordPatField::cast) {
+        let mut file_id = None;
+        for source in local.sources(sema.db) {
+            let source = source.source;
+            file_id = source.file_id.file_id();
+            if let Either::Left(pat) = source.value {
                 let name_range = pat.name().unwrap().syntax().text_range();
-                if let Some(name_ref) = pat_field.name_ref() {
-                    if new_name == name_ref.text() && pat.at_token().is_none() {
-                        // Foo { field: ref mut local } -> Foo { ref mut field }
-                        //       ^^^^^^ delete this
-                        //                      ^^^^^ replace this with `field`
-                        cov_mark::hit!(test_rename_local_put_init_shorthand_pat);
-                        edit.delete(
-                            name_ref
-                                .syntax()
-                                .text_range()
-                                .cover_offset(pat.syntax().text_range().start()),
-                        );
-                        edit.replace(name_range, name_ref.text().to_string());
+                // special cases required for renaming fields/locals in Record patterns
+                if let Some(pat_field) = pat.syntax().parent().and_then(ast::RecordPatField::cast) {
+                    if let Some(name_ref) = pat_field.name_ref() {
+                        if new_name == name_ref.text() && pat.at_token().is_none() {
+                            // Foo { field: ref mut local } -> Foo { ref mut field }
+                            //       ^^^^^^ delete this
+                            //                      ^^^^^ replace this with `field`
+                            cov_mark::hit!(test_rename_local_put_init_shorthand_pat);
+                            edit.delete(
+                                name_ref
+                                    .syntax()
+                                    .text_range()
+                                    .cover_offset(pat.syntax().text_range().start()),
+                            );
+                            edit.replace(name_range, name_ref.text().to_string());
+                        } else {
+                            // Foo { field: ref mut local @ local 2} -> Foo { field: ref mut new_name @ local2 }
+                            // Foo { field: ref mut local } -> Foo { field: ref mut new_name }
+                            //                      ^^^^^ replace this with `new_name`
+                            edit.replace(name_range, new_name.to_string());
+                        }
                     } else {
-                        // Foo { field: ref mut local @ local 2} -> Foo { field: ref mut new_name @ local2 }
-                        // Foo { field: ref mut local } -> Foo { field: ref mut new_name }
-                        //                      ^^^^^ replace this with `new_name`
+                        // Foo { ref mut field } -> Foo { field: ref mut new_name }
+                        //      ^ insert `field: `
+                        //               ^^^^^ replace this with `new_name`
+                        edit.insert(
+                            pat.syntax().text_range().start(),
+                            format!("{}: ", pat_field.field_name().unwrap()),
+                        );
                         edit.replace(name_range, new_name.to_string());
                     }
                 } else {
-                    // Foo { ref mut field } -> Foo { field: ref mut new_name }
-                    //      ^ insert `field: `
-                    //               ^^^^^ replace this with `new_name`
-                    edit.insert(
-                        pat.syntax().text_range().start(),
-                        format!("{}: ", pat_field.field_name().unwrap()),
-                    );
                     edit.replace(name_range, new_name.to_string());
                 }
             }
         }
+        let Some(file_id) = file_id else { bail!("No file available to rename") };
+        return Ok((file_id, edit.finish()));
     }
-    if edit.is_empty() {
-        let (range, new_name) = match def {
-            Definition::GenericParam(hir::GenericParam::LifetimeParam(_))
-            | Definition::Label(_) => (
-                TextRange::new(range.start() + syntax::TextSize::from(1), range.end()),
-                new_name.strip_prefix('\'').unwrap_or(new_name).to_owned(),
-            ),
-            _ => (range, new_name.to_owned()),
-        };
-        edit.replace(range, new_name);
-    }
+    let FileRange { file_id, range } = def
+        .range_for_rename(sema)
+        .ok_or_else(|| format_err!("No identifier available to rename"))?;
+    let (range, new_name) = match def {
+        Definition::GenericParam(hir::GenericParam::LifetimeParam(_)) | Definition::Label(_) => (
+            TextRange::new(range.start() + syntax::TextSize::from(1), range.end()),
+            new_name.strip_prefix('\'').unwrap_or(new_name).to_owned(),
+        ),
+        _ => (range, new_name.to_owned()),
+    };
+    edit.replace(range, new_name);
     Ok((file_id, edit.finish()))
 }
 
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/search.rs b/src/tools/rust-analyzer/crates/ide-db/src/search.rs
index c18a27f17d2..12f5e4e2a23 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/search.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/search.rs
@@ -244,14 +244,14 @@ impl Definition {
                 DefWithBody::Variant(v) => v.source(db).map(|src| src.syntax().cloned()),
             };
             return match def {
-                Some(def) => SearchScope::file_range(def.as_ref().original_file_range(db)),
+                Some(def) => SearchScope::file_range(def.as_ref().original_file_range_full(db)),
                 None => SearchScope::single_file(file_id),
             };
         }
 
         if let Definition::SelfType(impl_) = self {
             return match impl_.source(db).map(|src| src.syntax().cloned()) {
-                Some(def) => SearchScope::file_range(def.as_ref().original_file_range(db)),
+                Some(def) => SearchScope::file_range(def.as_ref().original_file_range_full(db)),
                 None => SearchScope::single_file(file_id),
             };
         }
@@ -261,13 +261,14 @@ impl Definition {
                 hir::GenericDef::Function(it) => it.source(db).map(|src| src.syntax().cloned()),
                 hir::GenericDef::Adt(it) => it.source(db).map(|src| src.syntax().cloned()),
                 hir::GenericDef::Trait(it) => it.source(db).map(|src| src.syntax().cloned()),
+                hir::GenericDef::TraitAlias(it) => it.source(db).map(|src| src.syntax().cloned()),
                 hir::GenericDef::TypeAlias(it) => it.source(db).map(|src| src.syntax().cloned()),
                 hir::GenericDef::Impl(it) => it.source(db).map(|src| src.syntax().cloned()),
                 hir::GenericDef::Variant(it) => it.source(db).map(|src| src.syntax().cloned()),
                 hir::GenericDef::Const(it) => it.source(db).map(|src| src.syntax().cloned()),
             };
             return match def {
-                Some(def) => SearchScope::file_range(def.as_ref().original_file_range(db)),
+                Some(def) => SearchScope::file_range(def.as_ref().original_file_range_full(db)),
                 None => SearchScope::single_file(file_id),
             };
         }
@@ -318,10 +319,6 @@ impl Definition {
             sema,
             scope: None,
             include_self_kw_refs: None,
-            local_repr: match self {
-                Definition::Local(local) => Some(local.representative(sema.db)),
-                _ => None,
-            },
             search_self_mod: false,
         }
     }
@@ -336,9 +333,6 @@ pub struct FindUsages<'a> {
     assoc_item_container: Option<hir::AssocItemContainer>,
     /// whether to search for the `Self` type of the definition
     include_self_kw_refs: Option<hir::Type>,
-    /// the local representative for the local definition we are searching for
-    /// (this is required for finding all local declarations in a or-pattern)
-    local_repr: Option<hir::Local>,
     /// whether to search for the `self` module
     search_self_mod: bool,
 }
@@ -643,19 +637,6 @@ impl<'a> FindUsages<'a> {
         sink: &mut dyn FnMut(FileId, FileReference) -> bool,
     ) -> bool {
         match NameRefClass::classify(self.sema, name_ref) {
-            Some(NameRefClass::Definition(def @ Definition::Local(local)))
-                if matches!(
-                    self.local_repr, Some(repr) if repr == local.representative(self.sema.db)
-                ) =>
-            {
-                let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax());
-                let reference = FileReference {
-                    range,
-                    name: ast::NameLike::NameRef(name_ref.clone()),
-                    category: ReferenceCategory::new(&def, name_ref),
-                };
-                sink(file_id, reference)
-            }
             Some(NameRefClass::Definition(def))
                 if self.def == def
                     // is our def a trait assoc item? then we want to find all assoc items from trait impls of our trait
@@ -700,14 +681,16 @@ impl<'a> FindUsages<'a> {
                 }
             }
             Some(NameRefClass::FieldShorthand { local_ref: local, field_ref: field }) => {
-                let field = Definition::Field(field);
                 let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax());
+
+                let field = Definition::Field(field);
+                let local = Definition::Local(local);
                 let access = match self.def {
                     Definition::Field(_) if field == self.def => {
                         ReferenceCategory::new(&field, name_ref)
                     }
-                    Definition::Local(_) if matches!(self.local_repr, Some(repr) if repr == local.representative(self.sema.db)) => {
-                        ReferenceCategory::new(&Definition::Local(local), name_ref)
+                    Definition::Local(_) if local == self.def => {
+                        ReferenceCategory::new(&local, name_ref)
                     }
                     _ => return false,
                 };
@@ -751,21 +734,6 @@ impl<'a> FindUsages<'a> {
                 };
                 sink(file_id, reference)
             }
-            Some(NameClass::Definition(def @ Definition::Local(local))) if def != self.def => {
-                if matches!(
-                    self.local_repr,
-                    Some(repr) if local.representative(self.sema.db) == repr
-                ) {
-                    let FileRange { file_id, range } = self.sema.original_range(name.syntax());
-                    let reference = FileReference {
-                        range,
-                        name: ast::NameLike::Name(name.clone()),
-                        category: None,
-                    };
-                    return sink(file_id, reference);
-                }
-                false
-            }
             Some(NameClass::Definition(def)) if def != self.def => {
                 match (&self.assoc_item_container, self.def) {
                     // for type aliases we always want to reference the trait def and all the trait impl counterparts
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs b/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs
index 8e338061df4..936354f2961 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs
@@ -83,6 +83,14 @@ impl From<NoHashHashMap<FileId, TextEdit>> for SourceChange {
     }
 }
 
+impl FromIterator<(FileId, TextEdit)> for SourceChange {
+    fn from_iter<T: IntoIterator<Item = (FileId, TextEdit)>>(iter: T) -> Self {
+        let mut this = SourceChange::default();
+        this.extend(iter);
+        this
+    }
+}
+
 pub struct SourceChangeBuilder {
     pub edit: TextEditBuilder,
     pub file_id: FileId,
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/use_trivial_contructor.rs b/src/tools/rust-analyzer/crates/ide-db/src/use_trivial_constructor.rs
index 39431bed382..39431bed382 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/use_trivial_contructor.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/use_trivial_constructor.rs
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs
index 10e637979f2..114face2dca 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs
@@ -7,10 +7,15 @@ pub(crate) fn break_outside_of_loop(
     ctx: &DiagnosticsContext<'_>,
     d: &hir::BreakOutsideOfLoop,
 ) -> Diagnostic {
-    let construct = if d.is_break { "break" } else { "continue" };
+    let message = if d.bad_value_break {
+        "can't break with a value in this position".to_owned()
+    } else {
+        let construct = if d.is_break { "break" } else { "continue" };
+        format!("{construct} outside of loop")
+    };
     Diagnostic::new(
         "break-outside-of-loop",
-        format!("{construct} outside of loop"),
+        message,
         ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range,
     )
 }
@@ -135,4 +140,18 @@ fn foo() {
 "#,
         );
     }
+
+    #[test]
+    fn value_break_in_for_loop() {
+        check_diagnostics(
+            r#"
+fn test() {
+    for _ in [()] {
+        break 3;
+     // ^^^^^^^ error: can't break with a value in this position
+    }
+}
+"#,
+        );
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/expected_function.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/expected_function.rs
new file mode 100644
index 00000000000..d2f27664d6f
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/expected_function.rs
@@ -0,0 +1,39 @@
+use hir::HirDisplay;
+
+use crate::{Diagnostic, DiagnosticsContext};
+
+// Diagnostic: expected-function
+//
+// This diagnostic is triggered if a call is made on something that is not callable.
+pub(crate) fn expected_function(
+    ctx: &DiagnosticsContext<'_>,
+    d: &hir::ExpectedFunction,
+) -> Diagnostic {
+    Diagnostic::new(
+        "expected-function",
+        format!("expected function, found {}", d.found.display(ctx.sema.db)),
+        ctx.sema.diagnostics_display_range(d.call.clone().map(|it| it.into())).range,
+    )
+    .experimental()
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::tests::check_diagnostics;
+
+    #[test]
+    fn smoke_test() {
+        check_diagnostics(
+            r#"
+fn foo() {
+    let x = 3;
+    x();
+ // ^^^ error: expected function, found i32
+    ""();
+ // ^^^^ error: expected function, found &str
+    foo();
+}
+"#,
+        );
+    }
+}
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs
index 43af4d4f16a..14039087b3f 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs
@@ -5,7 +5,7 @@ use hir::{
 };
 use ide_db::{
     assists::Assist, famous_defs::FamousDefs, imports::import_assets::item_for_path_search,
-    source_change::SourceChange, use_trivial_contructor::use_trivial_constructor, FxHashMap,
+    source_change::SourceChange, use_trivial_constructor::use_trivial_constructor, FxHashMap,
 };
 use stdx::format_to;
 use syntax::{
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs
index c24430ce604..ac4463331f2 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs
@@ -1,5 +1,3 @@
-use hir::InFile;
-
 use crate::{Diagnostic, DiagnosticsContext};
 
 // Diagnostic: missing-match-arm
@@ -12,7 +10,7 @@ pub(crate) fn missing_match_arms(
     Diagnostic::new(
         "missing-match-arm",
         format!("missing match arm: {}", d.uncovered_patterns),
-        ctx.sema.diagnostics_display_range(InFile::new(d.file, d.match_expr.clone().into())).range,
+        ctx.sema.diagnostics_display_range(d.scrutinee_expr.clone().map(Into::into)).range,
     )
 }
 
@@ -1038,7 +1036,6 @@ fn main() {
         #[test]
         fn reference_patterns_in_fields() {
             cov_mark::check_count!(validate_match_bailed_out, 2);
-
             check_diagnostics(
                 r#"
 fn main() {
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs
new file mode 100644
index 00000000000..84189a5d560
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs
@@ -0,0 +1,625 @@
+use ide_db::source_change::SourceChange;
+use syntax::{AstNode, SyntaxKind, SyntaxNode, SyntaxToken, T};
+use text_edit::TextEdit;
+
+use crate::{fix, Diagnostic, DiagnosticsContext, Severity};
+
+// Diagnostic: need-mut
+//
+// This diagnostic is triggered on mutating an immutable variable.
+pub(crate) fn need_mut(ctx: &DiagnosticsContext<'_>, d: &hir::NeedMut) -> Diagnostic {
+    let fixes = (|| {
+        if d.local.is_ref(ctx.sema.db) {
+            // There is no simple way to add `mut` to `ref x` and `ref mut x`
+            return None;
+        }
+        let file_id = d.span.file_id.file_id()?;
+        let mut edit_builder = TextEdit::builder();
+        let use_range = d.span.value.text_range();
+        for source in d.local.sources(ctx.sema.db) {
+            let Some(ast) = source.name() else { continue };
+            edit_builder.insert(ast.syntax().text_range().start(), "mut ".to_string());
+        }
+        let edit = edit_builder.finish();
+        Some(vec![fix(
+            "add_mut",
+            "Change it to be mutable",
+            SourceChange::from_text_edit(file_id, edit),
+            use_range,
+        )])
+    })();
+    Diagnostic::new(
+        "need-mut",
+        format!("cannot mutate immutable variable `{}`", d.local.name(ctx.sema.db)),
+        ctx.sema.diagnostics_display_range(d.span.clone()).range,
+    )
+    .with_fixes(fixes)
+}
+
+// Diagnostic: unused-mut
+//
+// This diagnostic is triggered when a mutable variable isn't actually mutated.
+pub(crate) fn unused_mut(ctx: &DiagnosticsContext<'_>, d: &hir::UnusedMut) -> Diagnostic {
+    let ast = d.local.primary_source(ctx.sema.db).syntax_ptr();
+    let fixes = (|| {
+        let file_id = ast.file_id.file_id()?;
+        let mut edit_builder = TextEdit::builder();
+        let use_range = ast.value.text_range();
+        for source in d.local.sources(ctx.sema.db) {
+            let ast = source.syntax();
+            let Some(mut_token) = token(ast, T![mut]) else { continue };
+            edit_builder.delete(mut_token.text_range());
+            if let Some(token) = mut_token.next_token() {
+                if token.kind() == SyntaxKind::WHITESPACE {
+                    edit_builder.delete(token.text_range());
+                }
+            }
+        }
+        let edit = edit_builder.finish();
+        Some(vec![fix(
+            "remove_mut",
+            "Remove unnecessary `mut`",
+            SourceChange::from_text_edit(file_id, edit),
+            use_range,
+        )])
+    })();
+    let ast = d.local.primary_source(ctx.sema.db).syntax_ptr();
+    Diagnostic::new(
+        "unused-mut",
+        "variable does not need to be mutable",
+        ctx.sema.diagnostics_display_range(ast).range,
+    )
+    .severity(Severity::WeakWarning)
+    .experimental() // Not supporting `#[allow(unused_mut)]` leads to false positive.
+    .with_fixes(fixes)
+}
+
+pub(super) fn token(parent: &SyntaxNode, kind: SyntaxKind) -> Option<SyntaxToken> {
+    parent.children_with_tokens().filter_map(|it| it.into_token()).find(|it| it.kind() == kind)
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::tests::{check_diagnostics, check_fix};
+
+    #[test]
+    fn unused_mut_simple() {
+        check_diagnostics(
+            r#"
+fn f(_: i32) {}
+fn main() {
+    let mut x = 2;
+      //^^^^^ 💡 weak: variable does not need to be mutable
+    f(x);
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn no_false_positive_simple() {
+        check_diagnostics(
+            r#"
+fn f(_: i32) {}
+fn main() {
+    let x = 2;
+    f(x);
+}
+"#,
+        );
+        check_diagnostics(
+            r#"
+fn f(_: i32) {}
+fn main() {
+    let mut x = 2;
+    x = 5;
+    f(x);
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn multiple_errors_for_single_variable() {
+        check_diagnostics(
+            r#"
+fn f(_: i32) {}
+fn main() {
+    let x = 2;
+    x = 10;
+  //^^^^^^ 💡 error: cannot mutate immutable variable `x`
+    x = 5;
+  //^^^^^ 💡 error: cannot mutate immutable variable `x`
+    &mut x;
+  //^^^^^^ 💡 error: cannot mutate immutable variable `x`
+    f(x);
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn unused_mut_fix() {
+        check_fix(
+            r#"
+fn f(_: i32) {}
+fn main() {
+    let mu$0t x = 2;
+    f(x);
+}
+"#,
+            r#"
+fn f(_: i32) {}
+fn main() {
+    let x = 2;
+    f(x);
+}
+"#,
+        );
+        check_fix(
+            r#"
+fn f(_: i32) {}
+fn main() {
+    let ((mu$0t x, _) | (_, mut x)) = (2, 3);
+    f(x);
+}
+"#,
+            r#"
+fn f(_: i32) {}
+fn main() {
+    let ((x, _) | (_, x)) = (2, 3);
+    f(x);
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn need_mut_fix() {
+        check_fix(
+            r#"
+fn f(_: i32) {}
+fn main() {
+    let x = 2;
+    x$0 = 5;
+    f(x);
+}
+"#,
+            r#"
+fn f(_: i32) {}
+fn main() {
+    let mut x = 2;
+    x = 5;
+    f(x);
+}
+"#,
+        );
+        check_fix(
+            r#"
+fn f(_: i32) {}
+fn main() {
+    let ((x, _) | (_, x)) = (2, 3);
+    x =$0 4;
+    f(x);
+}
+"#,
+            r#"
+fn f(_: i32) {}
+fn main() {
+    let ((mut x, _) | (_, mut x)) = (2, 3);
+    x = 4;
+    f(x);
+}
+"#,
+        );
+
+        check_fix(
+            r#"
+struct Foo(i32);
+
+impl Foo {
+    fn foo(self) {
+        self = Fo$0o(5);
+    }
+}
+"#,
+            r#"
+struct Foo(i32);
+
+impl Foo {
+    fn foo(mut self) {
+        self = Foo(5);
+    }
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn need_mut_fix_not_applicable_on_ref() {
+        check_diagnostics(
+            r#"
+fn main() {
+    let ref x = 2;
+    x = &5;
+  //^^^^^^ error: cannot mutate immutable variable `x`
+}
+"#,
+        );
+        check_diagnostics(
+            r#"
+fn main() {
+    let ref mut x = 2;
+    x = &mut 5;
+  //^^^^^^^^^^ error: cannot mutate immutable variable `x`
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn field_mutate() {
+        check_diagnostics(
+            r#"
+fn f(_: i32) {}
+fn main() {
+    let mut x = (2, 7);
+      //^^^^^ 💡 weak: variable does not need to be mutable
+    f(x.1);
+}
+"#,
+        );
+        check_diagnostics(
+            r#"
+fn f(_: i32) {}
+fn main() {
+    let mut x = (2, 7);
+    x.0 = 5;
+    f(x.1);
+}
+"#,
+        );
+        check_diagnostics(
+            r#"
+fn f(_: i32) {}
+fn main() {
+    let x = (2, 7);
+    x.0 = 5;
+  //^^^^^^^ 💡 error: cannot mutate immutable variable `x`
+    f(x.1);
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn mutable_reference() {
+        check_diagnostics(
+            r#"
+fn main() {
+    let mut x = &mut 2;
+      //^^^^^ 💡 weak: variable does not need to be mutable
+    *x = 5;
+}
+"#,
+        );
+        check_diagnostics(
+            r#"
+fn main() {
+    let x = 2;
+    &mut x;
+  //^^^^^^ 💡 error: cannot mutate immutable variable `x`
+}
+"#,
+        );
+        check_diagnostics(
+            r#"
+fn main() {
+    let x_own = 2;
+    let ref mut x_ref = x_own;
+      //^^^^^^^^^^^^^ 💡 error: cannot mutate immutable variable `x_own`
+}
+"#,
+        );
+        check_diagnostics(
+            r#"
+struct Foo;
+impl Foo {
+    fn method(&mut self, x: i32) {}
+}
+fn main() {
+    let x = Foo;
+    x.method(2);
+  //^ 💡 error: cannot mutate immutable variable `x`
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn regression_14310() {
+        check_diagnostics(
+            r#"
+            fn clone(mut i: &!) -> ! {
+                   //^^^^^ 💡 weak: variable does not need to be mutable
+                *i
+            }
+        "#,
+        );
+    }
+
+    #[test]
+    fn match_bindings() {
+        check_diagnostics(
+            r#"
+fn main() {
+    match (2, 3) {
+        (x, mut y) => {
+          //^^^^^ 💡 weak: variable does not need to be mutable
+            x = 7;
+          //^^^^^ 💡 error: cannot mutate immutable variable `x`
+        }
+    }
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn mutation_in_dead_code() {
+        // This one is interesting. Dead code is not represented at all in the MIR, so
+        // there would be no mutablility error for locals in dead code. Rustc tries to
+        // not emit `unused_mut` in this case, but since it works without `mut`, and
+        // special casing it is not trivial, we emit it.
+        check_diagnostics(
+            r#"
+fn main() {
+    return;
+    let mut x = 2;
+      //^^^^^ 💡 weak: variable does not need to be mutable
+    &mut x;
+}
+"#,
+        );
+        check_diagnostics(
+            r#"
+fn main() {
+    loop {}
+    let mut x = 2;
+      //^^^^^ 💡 weak: variable does not need to be mutable
+    &mut x;
+}
+"#,
+        );
+        check_diagnostics(
+            r#"
+enum X {}
+fn g() -> X {
+    loop {}
+}
+fn f() -> ! {
+    loop {}
+}
+fn main(b: bool) {
+    if b {
+        f();
+    } else {
+        g();
+    }
+    let mut x = 2;
+      //^^^^^ 💡 weak: variable does not need to be mutable
+    &mut x;
+}
+"#,
+        );
+        check_diagnostics(
+            r#"
+fn main(b: bool) {
+    if b {
+        loop {}
+    } else {
+        return;
+    }
+    let mut x = 2;
+      //^^^^^ 💡 weak: variable does not need to be mutable
+    &mut x;
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn initialization_is_not_mutation() {
+        check_diagnostics(
+            r#"
+fn f(_: i32) {}
+fn main() {
+    let mut x;
+      //^^^^^ 💡 weak: variable does not need to be mutable
+    x = 5;
+    f(x);
+}
+"#,
+        );
+        check_diagnostics(
+            r#"
+fn f(_: i32) {}
+fn main(b: bool) {
+    let mut x;
+      //^^^^^ 💡 weak: variable does not need to be mutable
+    if b {
+        x = 1;
+    } else {
+        x = 3;
+    }
+    f(x);
+}
+"#,
+        );
+        check_diagnostics(
+            r#"
+fn f(_: i32) {}
+fn main(b: bool) {
+    let x;
+    if b {
+        x = 1;
+    }
+    x = 3;
+  //^^^^^ 💡 error: cannot mutate immutable variable `x`
+    f(x);
+}
+"#,
+        );
+        check_diagnostics(
+            r#"
+fn f(_: i32) {}
+fn main() {
+    let x;
+    loop {
+        x = 1;
+      //^^^^^ 💡 error: cannot mutate immutable variable `x`
+        f(x);
+    }
+}
+"#,
+        );
+        check_diagnostics(
+            r#"
+fn f(_: i32) {}
+fn main() {
+    loop {
+        let mut x = 1;
+          //^^^^^ 💡 weak: variable does not need to be mutable
+        f(x);
+        if let mut y = 2 {
+             //^^^^^ 💡 weak: variable does not need to be mutable
+            f(y);
+        }
+        match 3 {
+            mut z => f(z),
+          //^^^^^ 💡 weak: variable does not need to be mutable
+        }
+    }
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn function_arguments_are_initialized() {
+        check_diagnostics(
+            r#"
+fn f(mut x: i32) {
+   //^^^^^ 💡 weak: variable does not need to be mutable
+}
+"#,
+        );
+        check_diagnostics(
+            r#"
+fn f(x: i32) {
+   x = 5;
+ //^^^^^ 💡 error: cannot mutate immutable variable `x`
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn for_loop() {
+        check_diagnostics(
+            r#"
+//- minicore: iterators
+fn f(x: [(i32, u8); 10]) {
+    for (a, mut b) in x {
+          //^^^^^ 💡 weak: variable does not need to be mutable
+        a = 2;
+      //^^^^^ 💡 error: cannot mutate immutable variable `a`
+    }
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn overloaded_deref() {
+        // FIXME: check for false negative
+        check_diagnostics(
+            r#"
+//- minicore: deref_mut
+use core::ops::{Deref, DerefMut};
+
+struct Foo;
+impl Deref for Foo {
+    type Target = i32;
+    fn deref(&self) -> &i32 {
+        &5
+    }
+}
+impl DerefMut for Foo {
+    fn deref_mut(&mut self) -> &mut i32 {
+        &mut 5
+    }
+}
+fn f() {
+    let x = Foo;
+    let y = &*x;
+    let x = Foo;
+    let mut x = Foo;
+    let y: &mut i32 = &mut x;
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn or_pattern() {
+        check_diagnostics(
+            r#"
+//- minicore: option
+fn f(_: i32) {}
+fn main() {
+    let ((Some(mut x), None) | (_, Some(mut x))) = (None, Some(7));
+             //^^^^^ 💡 weak: variable does not need to be mutable
+    f(x);
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn or_pattern_no_terminator() {
+        check_diagnostics(
+            r#"
+enum Foo {
+    A, B, C, D
+}
+
+use Foo::*;
+
+fn f(inp: (Foo, Foo, Foo, Foo)) {
+    let ((A, B, _, x) | (B, C | D, x, _)) = inp else {
+        return;
+    };
+    x = B;
+  //^^^^^ 💡 error: cannot mutate immutable variable `x`
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn respect_allow_unused_mut() {
+        // FIXME: respect
+        check_diagnostics(
+            r#"
+fn f(_: i32) {}
+fn main() {
+    #[allow(unused_mut)]
+    let mut x = 2;
+      //^^^^^ 💡 weak: variable does not need to be mutable
+    f(x);
+}
+"#,
+        );
+    }
+}
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_assoc_item.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_assoc_item.rs
index 0b3121c765d..67da5c7f27d 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_assoc_item.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_assoc_item.rs
@@ -118,4 +118,42 @@ fn main(s: module::Struct) {
 "#,
         );
     }
+
+    #[test]
+    fn can_see_through_top_level_anonymous_const() {
+        // regression test for #14046.
+        check_diagnostics(
+            r#"
+struct S;
+mod m {
+    const _: () = {
+        impl crate::S {
+            pub(crate) fn method(self) {}
+            pub(crate) const A: usize = 42;
+        }
+    };
+    mod inner {
+        const _: () = {
+            impl crate::S {
+                pub(crate) fn method2(self) {}
+                pub(crate) const B: usize = 42;
+                pub(super) fn private(self) {}
+                pub(super) const PRIVATE: usize = 42;
+            }
+        };
+    }
+}
+fn main() {
+    S.method();
+    S::A;
+    S.method2();
+    S::B;
+    S.private();
+  //^^^^^^^^^^^ error: function `private` is private
+    S::PRIVATE;
+  //^^^^^^^^^^ error: const `PRIVATE` is private
+}
+"#,
+        );
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs
index 9826e1c707e..a0c276cc332 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs
@@ -55,7 +55,18 @@ fn fixes(
 
 #[cfg(test)]
 mod tests {
-    use crate::tests::{check_diagnostics, check_fix};
+    use crate::{
+        tests::{check_diagnostics_with_config, check_fix},
+        DiagnosticsConfig,
+    };
+
+    #[track_caller]
+    pub(crate) fn check_diagnostics(ra_fixture: &str) {
+        let mut config = DiagnosticsConfig::test_sample();
+        config.disabled.insert("inactive-code".to_string());
+        config.disabled.insert("unresolved-method".to_string());
+        check_diagnostics_with_config(config, ra_fixture)
+    }
 
     #[test]
     fn replace_filter_map_next_with_find_map2() {
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs
index 2adae165e4d..b57a13e53e6 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs
@@ -1,8 +1,9 @@
-use hir::{db::AstDatabase, HirDisplay, Type};
+use either::Either;
+use hir::{db::AstDatabase, HirDisplay, InFile, Type};
 use ide_db::{famous_defs::FamousDefs, source_change::SourceChange};
 use syntax::{
     ast::{self, BlockExpr, ExprStmt},
-    AstNode,
+    AstNode, AstPtr,
 };
 use text_edit::TextEdit;
 
@@ -10,19 +11,23 @@ use crate::{adjusted_display_range, fix, Assist, Diagnostic, DiagnosticsContext}
 
 // Diagnostic: type-mismatch
 //
-// This diagnostic is triggered when the type of an expression does not match
+// This diagnostic is triggered when the type of an expression or pattern does not match
 // the expected type.
 pub(crate) fn type_mismatch(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch) -> Diagnostic {
-    let display_range = adjusted_display_range::<ast::BlockExpr>(
-        ctx,
-        d.expr.clone().map(|it| it.into()),
-        &|block| {
-            let r_curly_range = block.stmt_list()?.r_curly_token()?.text_range();
-            cov_mark::hit!(type_mismatch_on_block);
-            Some(r_curly_range)
-        },
-    );
-
+    let display_range = match &d.expr_or_pat {
+        Either::Left(expr) => adjusted_display_range::<ast::BlockExpr>(
+            ctx,
+            expr.clone().map(|it| it.into()),
+            &|block| {
+                let r_curly_range = block.stmt_list()?.r_curly_token()?.text_range();
+                cov_mark::hit!(type_mismatch_on_block);
+                Some(r_curly_range)
+            },
+        ),
+        Either::Right(pat) => {
+            ctx.sema.diagnostics_display_range(pat.clone().map(|it| it.into())).range
+        }
+    };
     let mut diag = Diagnostic::new(
         "type-mismatch",
         format!(
@@ -42,10 +47,15 @@ pub(crate) fn type_mismatch(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch)
 fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch) -> Option<Vec<Assist>> {
     let mut fixes = Vec::new();
 
-    add_reference(ctx, d, &mut fixes);
-    add_missing_ok_or_some(ctx, d, &mut fixes);
-    remove_semicolon(ctx, d, &mut fixes);
-    str_ref_to_owned(ctx, d, &mut fixes);
+    match &d.expr_or_pat {
+        Either::Left(expr_ptr) => {
+            add_reference(ctx, d, expr_ptr, &mut fixes);
+            add_missing_ok_or_some(ctx, d, expr_ptr, &mut fixes);
+            remove_semicolon(ctx, d, expr_ptr, &mut fixes);
+            str_ref_to_owned(ctx, d, expr_ptr, &mut fixes);
+        }
+        Either::Right(_pat_ptr) => {}
+    }
 
     if fixes.is_empty() {
         None
@@ -57,9 +67,10 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch) -> Option<Vec<Assi
 fn add_reference(
     ctx: &DiagnosticsContext<'_>,
     d: &hir::TypeMismatch,
+    expr_ptr: &InFile<AstPtr<ast::Expr>>,
     acc: &mut Vec<Assist>,
 ) -> Option<()> {
-    let range = ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range;
+    let range = ctx.sema.diagnostics_display_range(expr_ptr.clone().map(|it| it.into())).range;
 
     let (_, mutability) = d.expected.as_reference()?;
     let actual_with_ref = Type::reference(&d.actual, mutability);
@@ -71,7 +82,7 @@ fn add_reference(
 
     let edit = TextEdit::insert(range.start(), ampersands);
     let source_change =
-        SourceChange::from_text_edit(d.expr.file_id.original_file(ctx.sema.db), edit);
+        SourceChange::from_text_edit(expr_ptr.file_id.original_file(ctx.sema.db), edit);
     acc.push(fix("add_reference_here", "Add reference here", source_change, range));
     Some(())
 }
@@ -79,10 +90,11 @@ fn add_reference(
 fn add_missing_ok_or_some(
     ctx: &DiagnosticsContext<'_>,
     d: &hir::TypeMismatch,
+    expr_ptr: &InFile<AstPtr<ast::Expr>>,
     acc: &mut Vec<Assist>,
 ) -> Option<()> {
-    let root = ctx.sema.db.parse_or_expand(d.expr.file_id)?;
-    let expr = d.expr.value.to_node(&root);
+    let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id)?;
+    let expr = expr_ptr.value.to_node(&root);
     let expr_range = expr.syntax().text_range();
     let scope = ctx.sema.scope(expr.syntax())?;
 
@@ -109,7 +121,7 @@ fn add_missing_ok_or_some(
     builder.insert(expr.syntax().text_range().start(), format!("{variant_name}("));
     builder.insert(expr.syntax().text_range().end(), ")".to_string());
     let source_change =
-        SourceChange::from_text_edit(d.expr.file_id.original_file(ctx.sema.db), builder.finish());
+        SourceChange::from_text_edit(expr_ptr.file_id.original_file(ctx.sema.db), builder.finish());
     let name = format!("Wrap in {variant_name}");
     acc.push(fix("wrap_in_constructor", &name, source_change, expr_range));
     Some(())
@@ -118,10 +130,11 @@ fn add_missing_ok_or_some(
 fn remove_semicolon(
     ctx: &DiagnosticsContext<'_>,
     d: &hir::TypeMismatch,
+    expr_ptr: &InFile<AstPtr<ast::Expr>>,
     acc: &mut Vec<Assist>,
 ) -> Option<()> {
-    let root = ctx.sema.db.parse_or_expand(d.expr.file_id)?;
-    let expr = d.expr.value.to_node(&root);
+    let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id)?;
+    let expr = expr_ptr.value.to_node(&root);
     if !d.actual.is_unit() {
         return None;
     }
@@ -136,7 +149,7 @@ fn remove_semicolon(
 
     let edit = TextEdit::delete(semicolon_range);
     let source_change =
-        SourceChange::from_text_edit(d.expr.file_id.original_file(ctx.sema.db), edit);
+        SourceChange::from_text_edit(expr_ptr.file_id.original_file(ctx.sema.db), edit);
 
     acc.push(fix("remove_semicolon", "Remove this semicolon", source_change, semicolon_range));
     Some(())
@@ -145,24 +158,26 @@ fn remove_semicolon(
 fn str_ref_to_owned(
     ctx: &DiagnosticsContext<'_>,
     d: &hir::TypeMismatch,
+    expr_ptr: &InFile<AstPtr<ast::Expr>>,
     acc: &mut Vec<Assist>,
 ) -> Option<()> {
     let expected = d.expected.display(ctx.sema.db);
     let actual = d.actual.display(ctx.sema.db);
 
+    // FIXME do this properly
     if expected.to_string() != "String" || actual.to_string() != "&str" {
         return None;
     }
 
-    let root = ctx.sema.db.parse_or_expand(d.expr.file_id)?;
-    let expr = d.expr.value.to_node(&root);
+    let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id)?;
+    let expr = expr_ptr.value.to_node(&root);
     let expr_range = expr.syntax().text_range();
 
     let to_owned = format!(".to_owned()");
 
     let edit = TextEdit::insert(expr.syntax().text_range().end(), to_owned);
     let source_change =
-        SourceChange::from_text_edit(d.expr.file_id.original_file(ctx.sema.db), edit);
+        SourceChange::from_text_edit(expr_ptr.file_id.original_file(ctx.sema.db), edit);
     acc.push(fix("str_ref_to_owned", "Add .to_owned() here", source_change, expr_range));
 
     Some(())
@@ -595,4 +610,19 @@ fn f() -> i32 {
 "#,
         );
     }
+
+    #[test]
+    fn type_mismatch_pat_smoke_test() {
+        check_diagnostics(
+            r#"
+fn f() {
+    let &() = &mut ();
+    match &() {
+        &9 => ()
+       //^ error: expected (), found i32
+    }
+}
+"#,
+        );
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs
new file mode 100644
index 00000000000..7de03416e56
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs
@@ -0,0 +1,148 @@
+use hir::{db::AstDatabase, HirDisplay, InFile};
+use ide_db::{
+    assists::{Assist, AssistId, AssistKind},
+    base_db::FileRange,
+    label::Label,
+    source_change::SourceChange,
+};
+use syntax::{ast, AstNode, AstPtr};
+use text_edit::TextEdit;
+
+use crate::{Diagnostic, DiagnosticsContext};
+
+// Diagnostic: unresolved-field
+//
+// This diagnostic is triggered if a field does not exist on a given type.
+pub(crate) fn unresolved_field(
+    ctx: &DiagnosticsContext<'_>,
+    d: &hir::UnresolvedField,
+) -> Diagnostic {
+    let method_suffix = if d.method_with_same_name_exists {
+        ", but a method with a similar name exists"
+    } else {
+        ""
+    };
+    Diagnostic::new(
+        "unresolved-field",
+        format!(
+            "no field `{}` on type `{}`{method_suffix}",
+            d.name,
+            d.receiver.display(ctx.sema.db)
+        ),
+        ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range,
+    )
+    .with_fixes(fixes(ctx, d))
+    .experimental()
+}
+
+fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedField) -> Option<Vec<Assist>> {
+    if d.method_with_same_name_exists {
+        method_fix(ctx, &d.expr)
+    } else {
+        // FIXME: add quickfix
+
+        None
+    }
+}
+
+// FIXME: We should fill out the call here, mvoe the cursor and trigger signature help
+fn method_fix(
+    ctx: &DiagnosticsContext<'_>,
+    expr_ptr: &InFile<AstPtr<ast::Expr>>,
+) -> Option<Vec<Assist>> {
+    let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id)?;
+    let expr = expr_ptr.value.to_node(&root);
+    let FileRange { range, file_id } = ctx.sema.original_range_opt(expr.syntax())?;
+    Some(vec![Assist {
+        id: AssistId("expected-field-found-method-call-fix", AssistKind::QuickFix),
+        label: Label::new("Use parentheses to call the method".to_string()),
+        group: None,
+        target: range,
+        source_change: Some(SourceChange::from_text_edit(
+            file_id,
+            TextEdit::insert(range.end(), "()".to_owned()),
+        )),
+        trigger_signature_help: false,
+    }])
+}
+#[cfg(test)]
+mod tests {
+    use crate::tests::check_diagnostics;
+
+    #[test]
+    fn smoke_test() {
+        check_diagnostics(
+            r#"
+fn main() {
+    ().foo;
+ // ^^^^^^ error: no field `foo` on type `()`
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn method_clash() {
+        check_diagnostics(
+            r#"
+struct Foo;
+impl Foo {
+    fn bar(&self) {}
+}
+fn foo() {
+    Foo.bar;
+ // ^^^^^^^ 💡 error: no field `bar` on type `Foo`, but a method with a similar name exists
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn method_trait_() {
+        check_diagnostics(
+            r#"
+struct Foo;
+trait Bar {
+    fn bar(&self) {}
+}
+impl Bar for Foo {}
+fn foo() {
+    Foo.bar;
+ // ^^^^^^^ 💡 error: no field `bar` on type `Foo`, but a method with a similar name exists
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn method_trait_2() {
+        check_diagnostics(
+            r#"
+struct Foo;
+trait Bar {
+    fn bar(&self);
+}
+impl Bar for Foo {
+    fn bar(&self) {}
+}
+fn foo() {
+    Foo.bar;
+ // ^^^^^^^ 💡 error: no field `bar` on type `Foo`, but a method with a similar name exists
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn no_diagnostic_on_unknown() {
+        check_diagnostics(
+            r#"
+fn foo() {
+    x.foo;
+    (&x).foo;
+    (&((x,),),).foo;
+}
+"#,
+        );
+    }
+}
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs
new file mode 100644
index 00000000000..4b0e64cb896
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs
@@ -0,0 +1,131 @@
+use hir::{db::AstDatabase, HirDisplay};
+use ide_db::{
+    assists::{Assist, AssistId, AssistKind},
+    base_db::FileRange,
+    label::Label,
+    source_change::SourceChange,
+};
+use syntax::{ast, AstNode, TextRange};
+use text_edit::TextEdit;
+
+use crate::{Diagnostic, DiagnosticsContext};
+
+// Diagnostic: unresolved-method
+//
+// This diagnostic is triggered if a method does not exist on a given type.
+pub(crate) fn unresolved_method(
+    ctx: &DiagnosticsContext<'_>,
+    d: &hir::UnresolvedMethodCall,
+) -> Diagnostic {
+    let field_suffix = if d.field_with_same_name.is_some() {
+        ", but a field with a similar name exists"
+    } else {
+        ""
+    };
+    Diagnostic::new(
+        "unresolved-method",
+        format!(
+            "no method `{}` on type `{}`{field_suffix}",
+            d.name,
+            d.receiver.display(ctx.sema.db)
+        ),
+        ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range,
+    )
+    .with_fixes(fixes(ctx, d))
+    .experimental()
+}
+
+fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedMethodCall) -> Option<Vec<Assist>> {
+    if let Some(ty) = &d.field_with_same_name {
+        field_fix(ctx, d, ty)
+    } else {
+        // FIXME: add quickfix
+        None
+    }
+}
+
+fn field_fix(
+    ctx: &DiagnosticsContext<'_>,
+    d: &hir::UnresolvedMethodCall,
+    ty: &hir::Type,
+) -> Option<Vec<Assist>> {
+    if !ty.impls_fnonce(ctx.sema.db) {
+        return None;
+    }
+    let expr_ptr = &d.expr;
+    let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id)?;
+    let expr = expr_ptr.value.to_node(&root);
+    let (file_id, range) = match expr {
+        ast::Expr::MethodCallExpr(mcall) => {
+            let FileRange { range, file_id } =
+                ctx.sema.original_range_opt(mcall.receiver()?.syntax())?;
+            let FileRange { range: range2, file_id: file_id2 } =
+                ctx.sema.original_range_opt(mcall.name_ref()?.syntax())?;
+            if file_id != file_id2 {
+                return None;
+            }
+            (file_id, TextRange::new(range.start(), range2.end()))
+        }
+        _ => return None,
+    };
+    Some(vec![Assist {
+        id: AssistId("expected-method-found-field-fix", AssistKind::QuickFix),
+        label: Label::new("Use parentheses to call the value of the field".to_string()),
+        group: None,
+        target: range,
+        source_change: Some(SourceChange::from_iter([
+            (file_id, TextEdit::insert(range.start(), "(".to_owned())),
+            (file_id, TextEdit::insert(range.end(), ")".to_owned())),
+        ])),
+        trigger_signature_help: false,
+    }])
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::tests::{check_diagnostics, check_fix};
+
+    #[test]
+    fn smoke_test() {
+        check_diagnostics(
+            r#"
+fn main() {
+    ().foo();
+ // ^^^^^^^^ error: no method `foo` on type `()`
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn field() {
+        check_diagnostics(
+            r#"
+struct Foo { bar: i32 }
+fn foo() {
+    Foo { bar: i32 }.bar();
+ // ^^^^^^^^^^^^^^^^^^^^^^ error: no method `bar` on type `Foo`, but a field with a similar name exists
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn callable_field() {
+        check_fix(
+            r#"
+//- minicore: fn
+struct Foo { bar: fn() }
+fn foo() {
+    Foo { bar: foo }.b$0ar();
+}
+"#,
+            r#"
+struct Foo { bar: fn() }
+fn foo() {
+    (Foo { bar: foo }.bar)();
+}
+"#,
+        );
+    }
+}
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs
index 64ba08ac883..f6c9b79c30c 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs
@@ -27,6 +27,7 @@
 
 mod handlers {
     pub(crate) mod break_outside_of_loop;
+    pub(crate) mod expected_function;
     pub(crate) mod inactive_code;
     pub(crate) mod incorrect_case;
     pub(crate) mod invalid_derive_target;
@@ -36,6 +37,7 @@ mod handlers {
     pub(crate) mod missing_fields;
     pub(crate) mod missing_match_arms;
     pub(crate) mod missing_unsafe;
+    pub(crate) mod mutability_errors;
     pub(crate) mod no_such_field;
     pub(crate) mod private_assoc_item;
     pub(crate) mod private_field;
@@ -43,6 +45,8 @@ mod handlers {
     pub(crate) mod type_mismatch;
     pub(crate) mod unimplemented_builtin_macro;
     pub(crate) mod unresolved_extern_crate;
+    pub(crate) mod unresolved_field;
+    pub(crate) mod unresolved_method;
     pub(crate) mod unresolved_import;
     pub(crate) mod unresolved_macro_call;
     pub(crate) mod unresolved_module;
@@ -248,6 +252,7 @@ pub fn diagnostics(
         #[rustfmt::skip]
         let d = match diag {
             AnyDiagnostic::BreakOutsideOfLoop(d) => handlers::break_outside_of_loop::break_outside_of_loop(&ctx, &d),
+            AnyDiagnostic::ExpectedFunction(d) => handlers::expected_function::expected_function(&ctx, &d),
             AnyDiagnostic::IncorrectCase(d) => handlers::incorrect_case::incorrect_case(&ctx, &d),
             AnyDiagnostic::MacroError(d) => handlers::macro_error::macro_error(&ctx, &d),
             AnyDiagnostic::MalformedDerive(d) => handlers::malformed_derive::malformed_derive(&ctx, &d),
@@ -267,7 +272,10 @@ pub fn diagnostics(
             AnyDiagnostic::UnresolvedModule(d) => handlers::unresolved_module::unresolved_module(&ctx, &d),
             AnyDiagnostic::UnresolvedProcMacro(d) => handlers::unresolved_proc_macro::unresolved_proc_macro(&ctx, &d, config.proc_macros_enabled, config.proc_attr_macros_enabled),
             AnyDiagnostic::InvalidDeriveTarget(d) => handlers::invalid_derive_target::invalid_derive_target(&ctx, &d),
-
+            AnyDiagnostic::UnresolvedField(d) => handlers::unresolved_field::unresolved_field(&ctx, &d),
+            AnyDiagnostic::UnresolvedMethodCall(d) => handlers::unresolved_method::unresolved_method(&ctx, &d),
+            AnyDiagnostic::NeedMut(d) => handlers::mutability_errors::need_mut(&ctx, &d),
+            AnyDiagnostic::UnusedMut(d) => handlers::mutability_errors::unused_mut(&ctx, &d),
             AnyDiagnostic::InactiveCode(d) => match handlers::inactive_code::inactive_code(&ctx, &d) {
                 Some(it) => it,
                 None => continue,
diff --git a/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs b/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs
index 57b5ab6abda..a8e88369088 100644
--- a/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs
+++ b/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs
@@ -561,7 +561,7 @@ impl<'db, 'sema> Matcher<'db, 'sema> {
                             .sema
                             .resolve_method_call_as_callable(code)
                             .and_then(|callable| callable.receiver_param(self.sema.db))
-                            .map(|self_param| self_param.kind())
+                            .map(|(self_param, _)| self_param.kind())
                             .unwrap_or(ast::SelfParamKind::Owned);
                     }
                 }
diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs
index b4a7f2b918a..fae25f31023 100644
--- a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs
@@ -107,7 +107,18 @@ pub(crate) fn remove_links(markdown: &str) -> String {
     out
 }
 
-/// Retrieve a link to documentation for the given symbol.
+// Feature: Open Docs
+//
+// Retrieve a link to documentation for the given symbol.
+//
+// The simplest way to use this feature is via the context menu. Right-click on
+// the selected item. The context menu opens. Select **Open Docs**.
+//
+// |===
+// | Editor  | Action Name
+//
+// | VS Code | **rust-analyzer: Open Docs**
+// |===
 pub(crate) fn external_docs(
     db: &RootDatabase,
     position: &FilePosition,
@@ -181,6 +192,7 @@ pub(crate) fn resolve_doc_path_for_def(
         Definition::Const(it) => it.resolve_doc_path(db, link, ns),
         Definition::Static(it) => it.resolve_doc_path(db, link, ns),
         Definition::Trait(it) => it.resolve_doc_path(db, link, ns),
+        Definition::TraitAlias(it) => it.resolve_doc_path(db, link, ns),
         Definition::TypeAlias(it) => it.resolve_doc_path(db, link, ns),
         Definition::Macro(it) => it.resolve_doc_path(db, link, ns),
         Definition::Field(it) => it.resolve_doc_path(db, link, ns),
@@ -493,6 +505,7 @@ fn filename_and_frag_for_def(
             None => String::from("index.html"),
         },
         Definition::Trait(t) => format!("trait.{}.html", t.name(db)),
+        Definition::TraitAlias(t) => format!("traitalias.{}.html", t.name(db)),
         Definition::TypeAlias(t) => format!("type.{}.html", t.name(db)),
         Definition::BuiltinType(t) => format!("primitive.{}.html", t.name()),
         Definition::Function(f) => format!("fn.{}.html", f.name(db)),
diff --git a/src/tools/rust-analyzer/crates/ide/src/file_structure.rs b/src/tools/rust-analyzer/crates/ide/src/file_structure.rs
index b23763dce86..a32ac35496a 100644
--- a/src/tools/rust-analyzer/crates/ide/src/file_structure.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/file_structure.rs
@@ -149,6 +149,7 @@ fn structure_node(node: &SyntaxNode) -> Option<StructureNode> {
             ast::Enum(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Enum)),
             ast::Variant(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Variant)),
             ast::Trait(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Trait)),
+            ast::TraitAlias(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::TraitAlias)),
             ast::Module(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Module)),
             ast::TypeAlias(it) => decl_with_type_ref(&it, it.ty(), StructureNodeKind::SymbolKind(SymbolKind::TypeAlias)),
             ast::RecordField(it) => decl_with_type_ref(&it, it.ty(), StructureNodeKind::SymbolKind(SymbolKind::Field)),
@@ -262,6 +263,8 @@ enum E { X, Y(i32) }
 type T = ();
 static S: i32 = 92;
 const C: i32 = 92;
+trait Tr {}
+trait Alias = Tr;
 
 impl E {}
 
@@ -459,9 +462,31 @@ fn g() {}
                     },
                     StructureNode {
                         parent: None,
+                        label: "Tr",
+                        navigation_range: 239..241,
+                        node_range: 233..244,
+                        kind: SymbolKind(
+                            Trait,
+                        ),
+                        detail: None,
+                        deprecated: false,
+                    },
+                    StructureNode {
+                        parent: None,
+                        label: "Alias",
+                        navigation_range: 251..256,
+                        node_range: 245..262,
+                        kind: SymbolKind(
+                            TraitAlias,
+                        ),
+                        detail: None,
+                        deprecated: false,
+                    },
+                    StructureNode {
+                        parent: None,
                         label: "impl E",
-                        navigation_range: 239..240,
-                        node_range: 234..243,
+                        navigation_range: 269..270,
+                        node_range: 264..273,
                         kind: SymbolKind(
                             Impl,
                         ),
@@ -471,8 +496,8 @@ fn g() {}
                     StructureNode {
                         parent: None,
                         label: "impl fmt::Debug for E",
-                        navigation_range: 265..266,
-                        node_range: 245..269,
+                        navigation_range: 295..296,
+                        node_range: 275..299,
                         kind: SymbolKind(
                             Impl,
                         ),
@@ -482,8 +507,8 @@ fn g() {}
                     StructureNode {
                         parent: None,
                         label: "mc",
-                        navigation_range: 284..286,
-                        node_range: 271..303,
+                        navigation_range: 314..316,
+                        node_range: 301..333,
                         kind: SymbolKind(
                             Macro,
                         ),
@@ -493,8 +518,8 @@ fn g() {}
                     StructureNode {
                         parent: None,
                         label: "mcexp",
-                        navigation_range: 334..339,
-                        node_range: 305..356,
+                        navigation_range: 364..369,
+                        node_range: 335..386,
                         kind: SymbolKind(
                             Macro,
                         ),
@@ -504,8 +529,8 @@ fn g() {}
                     StructureNode {
                         parent: None,
                         label: "mcexp",
-                        navigation_range: 387..392,
-                        node_range: 358..409,
+                        navigation_range: 417..422,
+                        node_range: 388..439,
                         kind: SymbolKind(
                             Macro,
                         ),
@@ -515,8 +540,8 @@ fn g() {}
                     StructureNode {
                         parent: None,
                         label: "obsolete",
-                        navigation_range: 428..436,
-                        node_range: 411..441,
+                        navigation_range: 458..466,
+                        node_range: 441..471,
                         kind: SymbolKind(
                             Function,
                         ),
@@ -528,8 +553,8 @@ fn g() {}
                     StructureNode {
                         parent: None,
                         label: "very_obsolete",
-                        navigation_range: 481..494,
-                        node_range: 443..499,
+                        navigation_range: 511..524,
+                        node_range: 473..529,
                         kind: SymbolKind(
                             Function,
                         ),
@@ -541,8 +566,8 @@ fn g() {}
                     StructureNode {
                         parent: None,
                         label: "Some region name",
-                        navigation_range: 501..528,
-                        node_range: 501..528,
+                        navigation_range: 531..558,
+                        node_range: 531..558,
                         kind: Region,
                         detail: None,
                         deprecated: false,
@@ -550,8 +575,8 @@ fn g() {}
                     StructureNode {
                         parent: None,
                         label: "m",
-                        navigation_range: 568..569,
-                        node_range: 543..606,
+                        navigation_range: 598..599,
+                        node_range: 573..636,
                         kind: SymbolKind(
                             Module,
                         ),
@@ -560,22 +585,22 @@ fn g() {}
                     },
                     StructureNode {
                         parent: Some(
-                            20,
+                            22,
                         ),
                         label: "dontpanic",
-                        navigation_range: 543..563,
-                        node_range: 543..563,
+                        navigation_range: 573..593,
+                        node_range: 573..593,
                         kind: Region,
                         detail: None,
                         deprecated: false,
                     },
                     StructureNode {
                         parent: Some(
-                            20,
+                            22,
                         ),
                         label: "f",
-                        navigation_range: 575..576,
-                        node_range: 572..581,
+                        navigation_range: 605..606,
+                        node_range: 602..611,
                         kind: SymbolKind(
                             Function,
                         ),
@@ -586,11 +611,11 @@ fn g() {}
                     },
                     StructureNode {
                         parent: Some(
-                            20,
+                            22,
                         ),
                         label: "g",
-                        navigation_range: 598..599,
-                        node_range: 582..604,
+                        navigation_range: 628..629,
+                        node_range: 612..634,
                         kind: SymbolKind(
                             Function,
                         ),
diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs
index 93019527f44..cf0819a2524 100644
--- a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs
@@ -766,6 +766,13 @@ trait Foo$0 { }
 
         check(
             r#"
+trait Foo$0 = ;
+    //^^^
+"#,
+        );
+
+        check(
+            r#"
 mod bar$0 { }
   //^^^
 "#,
@@ -1066,6 +1073,23 @@ fn f() -> impl Sub<Item$0 = u8> {}
     }
 
     #[test]
+    fn goto_def_for_module_declaration_in_path_if_types_and_values_same_name() {
+        check(
+            r#"
+mod bar {
+    pub struct Foo {}
+             //^^^
+    pub fn Foo() {}
+}
+
+fn baz() {
+    let _foo_enum: bar::Foo$0 = bar::Foo {};
+}
+        "#,
+        )
+    }
+
+    #[test]
     fn unknown_assoc_ty() {
         check_unresolved(
             r#"
@@ -1406,7 +1430,6 @@ include!("included.rs$0");
         );
     }
 
-    #[cfg(test)]
     mod goto_impl_of_trait_fn {
         use super::check;
         #[test]
diff --git a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs
index c889eb930f3..d88ffd25c40 100644
--- a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs
@@ -14,7 +14,7 @@ use syntax::{
     SyntaxNode, SyntaxToken, TextRange, T,
 };
 
-use crate::{references, NavigationTarget, TryToNav};
+use crate::{navigation_target::ToNav, references, NavigationTarget, TryToNav};
 
 #[derive(PartialEq, Eq, Hash)]
 pub struct HighlightedRange {
@@ -98,32 +98,39 @@ fn highlight_references(
             category: access,
         });
     let mut res = FxHashSet::default();
-
-    let mut def_to_hl_range = |def| {
-        let hl_range = match def {
-            Definition::Module(module) => {
-                Some(NavigationTarget::from_module_to_decl(sema.db, module))
-            }
-            def => def.try_to_nav(sema.db),
-        }
-        .filter(|decl| decl.file_id == file_id)
-        .and_then(|decl| decl.focus_range)
-        .map(|range| {
-            let category =
-                references::decl_mutability(&def, node, range).then_some(ReferenceCategory::Write);
-            HighlightedRange { range, category }
-        });
-        if let Some(hl_range) = hl_range {
-            res.insert(hl_range);
-        }
-    };
     for &def in &defs {
         match def {
-            Definition::Local(local) => local
-                .associated_locals(sema.db)
-                .iter()
-                .for_each(|&local| def_to_hl_range(Definition::Local(local))),
-            def => def_to_hl_range(def),
+            Definition::Local(local) => {
+                let category = local.is_mut(sema.db).then_some(ReferenceCategory::Write);
+                local
+                    .sources(sema.db)
+                    .into_iter()
+                    .map(|x| x.to_nav(sema.db))
+                    .filter(|decl| decl.file_id == file_id)
+                    .filter_map(|decl| decl.focus_range)
+                    .map(|range| HighlightedRange { range, category })
+                    .for_each(|x| {
+                        res.insert(x);
+                    });
+            }
+            def => {
+                let hl_range = match def {
+                    Definition::Module(module) => {
+                        Some(NavigationTarget::from_module_to_decl(sema.db, module))
+                    }
+                    def => def.try_to_nav(sema.db),
+                }
+                .filter(|decl| decl.file_id == file_id)
+                .and_then(|decl| decl.focus_range)
+                .map(|range| {
+                    let category = references::decl_mutability(&def, node, range)
+                        .then_some(ReferenceCategory::Write);
+                    HighlightedRange { range, category }
+                });
+                if let Some(hl_range) = hl_range {
+                    res.insert(hl_range);
+                }
+            }
         }
     }
 
diff --git a/src/tools/rust-analyzer/crates/ide/src/hover.rs b/src/tools/rust-analyzer/crates/ide/src/hover.rs
index 5f2c61f5b5f..64b2221bdea 100644
--- a/src/tools/rust-analyzer/crates/ide/src/hover.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/hover.rs
@@ -30,6 +30,7 @@ pub struct HoverConfig {
     pub documentation: bool,
     pub keywords: bool,
     pub format: HoverDocFormat,
+    pub interpret_tests: bool,
 }
 
 #[derive(Clone, Debug, PartialEq, Eq)]
diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs
index 22611cfb892..da725ce502b 100644
--- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs
@@ -3,7 +3,8 @@ use std::fmt::Display;
 
 use either::Either;
 use hir::{
-    Adt, AsAssocItem, AttributeTemplate, HasAttrs, HasSource, HirDisplay, Semantics, TypeInfo,
+    db::DefDatabase, Adt, AsAssocItem, AttributeTemplate, HasAttrs, HasSource, HirDisplay,
+    MirEvalError, Semantics, TypeInfo,
 };
 use ide_db::{
     base_db::SourceDatabase,
@@ -402,7 +403,20 @@ pub(super) fn definition(
             ))
         }),
         Definition::Module(it) => label_and_docs(db, it),
-        Definition::Function(it) => label_and_docs(db, it),
+        Definition::Function(it) => label_and_layout_info_and_docs(db, it, |_| {
+            if !config.interpret_tests {
+                return None;
+            }
+            match it.eval(db) {
+                Ok(()) => Some("pass".into()),
+                Err(MirEvalError::Panic) => Some("fail".into()),
+                Err(MirEvalError::MirLowerError(f, e)) => {
+                    let name = &db.function_data(f).name;
+                    Some(format!("error: fail to lower {name} due {e:?}"))
+                }
+                Err(e) => Some(format!("error: {e:?}")),
+            }
+        }),
         Definition::Adt(it) => label_and_layout_info_and_docs(db, it, |&it| {
             let layout = it.layout(db).ok()?;
             Some(format!("size = {}, align = {}", layout.size.bytes(), layout.align.abi.bytes()))
@@ -410,7 +424,7 @@ pub(super) fn definition(
         Definition::Variant(it) => label_value_and_docs(db, it, |&it| {
             if !it.parent_enum(db).is_data_carrying(db) {
                 match it.eval(db) {
-                    Ok(x) => Some(format!("{x}")),
+                    Ok(x) => Some(if x >= 10 { format!("{x} ({x:#X})") } else { format!("{x}") }),
                     Err(_) => it.value(db).map(|x| format!("{x:?}")),
                 }
             } else {
@@ -418,9 +432,9 @@ pub(super) fn definition(
             }
         }),
         Definition::Const(it) => label_value_and_docs(db, it, |it| {
-            let body = it.eval(db);
+            let body = it.render_eval(db);
             match body {
-                Ok(x) => Some(format!("{x}")),
+                Ok(x) => Some(x),
                 Err(_) => {
                     let source = it.source(db)?;
                     let mut body = source.value.body()?.syntax().clone();
@@ -440,6 +454,7 @@ pub(super) fn definition(
             Some(body.to_string())
         }),
         Definition::Trait(it) => label_and_docs(db, it),
+        Definition::TraitAlias(it) => label_and_docs(db, it),
         Definition::TypeAlias(it) => label_and_docs(db, it),
         Definition::BuiltinType(it) => {
             return famous_defs
@@ -620,8 +635,8 @@ fn local(db: &RootDatabase, it: hir::Local) -> Option<Markup> {
     let ty = it.ty(db);
     let ty = ty.display_truncated(db, None);
     let is_mut = if it.is_mut(db) { "mut " } else { "" };
-    let desc = match it.source(db).value {
-        Either::Left(ident) => {
+    let desc = match it.primary_source(db).into_ident_pat() {
+        Some(ident) => {
             let name = it.name(db);
             let let_kw = if ident
                 .syntax()
@@ -634,7 +649,7 @@ fn local(db: &RootDatabase, it: hir::Local) -> Option<Markup> {
             };
             format!("{let_kw}{is_mut}{name}: {ty}")
         }
-        Either::Right(_) => format!("{is_mut}self: {ty}"),
+        None => format!("{is_mut}self: {ty}"),
     };
     markup(None, desc, None)
 }
diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
index bd7ce2f1d0d..57bf0f9ad5f 100644
--- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
@@ -4,16 +4,19 @@ use syntax::TextRange;
 
 use crate::{fixture, HoverConfig, HoverDocFormat};
 
+const HOVER_BASE_CONFIG: HoverConfig = HoverConfig {
+    links_in_hover: false,
+    documentation: true,
+    format: HoverDocFormat::Markdown,
+    keywords: true,
+    interpret_tests: false,
+};
+
 fn check_hover_no_result(ra_fixture: &str) {
     let (analysis, position) = fixture::position(ra_fixture);
     let hover = analysis
         .hover(
-            &HoverConfig {
-                links_in_hover: true,
-                documentation: true,
-                keywords: true,
-                format: HoverDocFormat::Markdown,
-            },
+            &HoverConfig { links_in_hover: true, ..HOVER_BASE_CONFIG },
             FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) },
         )
         .unwrap();
@@ -25,12 +28,7 @@ fn check(ra_fixture: &str, expect: Expect) {
     let (analysis, position) = fixture::position(ra_fixture);
     let hover = analysis
         .hover(
-            &HoverConfig {
-                links_in_hover: true,
-                documentation: true,
-                keywords: true,
-                format: HoverDocFormat::Markdown,
-            },
+            &HoverConfig { links_in_hover: true, ..HOVER_BASE_CONFIG },
             FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) },
         )
         .unwrap()
@@ -47,12 +45,7 @@ fn check_hover_no_links(ra_fixture: &str, expect: Expect) {
     let (analysis, position) = fixture::position(ra_fixture);
     let hover = analysis
         .hover(
-            &HoverConfig {
-                links_in_hover: false,
-                documentation: true,
-                keywords: true,
-                format: HoverDocFormat::Markdown,
-            },
+            &HOVER_BASE_CONFIG,
             FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) },
         )
         .unwrap()
@@ -71,9 +64,8 @@ fn check_hover_no_markdown(ra_fixture: &str, expect: Expect) {
         .hover(
             &HoverConfig {
                 links_in_hover: true,
-                documentation: true,
-                keywords: true,
                 format: HoverDocFormat::PlainText,
+                ..HOVER_BASE_CONFIG
             },
             FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) },
         )
@@ -91,12 +83,7 @@ fn check_actions(ra_fixture: &str, expect: Expect) {
     let (analysis, file_id, position) = fixture::range_or_position(ra_fixture);
     let hover = analysis
         .hover(
-            &HoverConfig {
-                links_in_hover: true,
-                documentation: true,
-                keywords: true,
-                format: HoverDocFormat::Markdown,
-            },
+            &HoverConfig { links_in_hover: true, ..HOVER_BASE_CONFIG },
             FileRange { file_id, range: position.range_or_empty() },
         )
         .unwrap()
@@ -106,34 +93,13 @@ fn check_actions(ra_fixture: &str, expect: Expect) {
 
 fn check_hover_range(ra_fixture: &str, expect: Expect) {
     let (analysis, range) = fixture::range(ra_fixture);
-    let hover = analysis
-        .hover(
-            &HoverConfig {
-                links_in_hover: false,
-                documentation: true,
-                keywords: true,
-                format: HoverDocFormat::Markdown,
-            },
-            range,
-        )
-        .unwrap()
-        .unwrap();
+    let hover = analysis.hover(&HOVER_BASE_CONFIG, range).unwrap().unwrap();
     expect.assert_eq(hover.info.markup.as_str())
 }
 
 fn check_hover_range_no_results(ra_fixture: &str) {
     let (analysis, range) = fixture::range(ra_fixture);
-    let hover = analysis
-        .hover(
-            &HoverConfig {
-                links_in_hover: false,
-                documentation: true,
-                keywords: true,
-                format: HoverDocFormat::Markdown,
-            },
-            range,
-        )
-        .unwrap();
+    let hover = analysis.hover(&HOVER_BASE_CONFIG, range).unwrap();
     assert!(hover.is_none());
 }
 
@@ -490,7 +456,6 @@ fn hover_field_offset() {
     // Hovering over the field when instantiating
     check(
         r#"
-//- /main.rs target_data_layout:e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128
 struct Foo { fiel$0d_a: u8, field_b: i32, field_c: i16 }
 "#,
         expect![[r#"
@@ -512,7 +477,6 @@ fn hover_shows_struct_field_info() {
     // Hovering over the field when instantiating
     check(
         r#"
-//- /main.rs target_data_layout:e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128
 struct Foo { field_a: u32 }
 
 fn main() {
@@ -535,7 +499,6 @@ fn main() {
     // Hovering over the field in the definition
     check(
         r#"
-//- /main.rs target_data_layout:e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128
 struct Foo { field_a$0: u32 }
 
 fn main() {
@@ -611,6 +574,27 @@ const foo$0: u32 = {
 }
 
 #[test]
+fn hover_eval_complex_constants() {
+    check(
+        r#"
+        struct X { f1: (), f2: i32 }
+        const foo$0: (i8, X, i64) = (1, X { f2: 5 - 1, f1: () }, 1 - 2);
+        "#,
+        expect![[r#"
+            *foo*
+
+            ```rust
+            test
+            ```
+
+            ```rust
+            const foo: (i8, X, i64) = (1, X { f1: (), f2: 4 }, -1)
+            ```
+        "#]],
+    );
+}
+
+#[test]
 fn hover_default_generic_types() {
     check(
         r#"
@@ -1467,8 +1451,6 @@ fn my() {}
 fn test_hover_struct_doc_comment() {
     check(
         r#"
-//- /main.rs target_data_layout:e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128
-
 /// This is an example
 /// multiline doc
 ///
@@ -1527,7 +1509,7 @@ fn foo() { let bar = Ba$0r; }
             ```
 
             ```rust
-            struct Bar
+            struct Bar // size = 0, align = 1
             ```
 
             ---
@@ -1556,7 +1538,7 @@ fn foo() { let bar = Ba$0r; }
             ```
 
             ```rust
-            struct Bar
+            struct Bar // size = 0, align = 1
             ```
 
             ---
@@ -1584,7 +1566,7 @@ pub struct B$0ar
             ```
 
             ```rust
-            pub struct Bar
+            pub struct Bar // size = 0, align = 1
             ```
 
             ---
@@ -1611,7 +1593,7 @@ pub struct B$0ar
             ```
 
             ```rust
-            pub struct Bar
+            pub struct Bar // size = 0, align = 1
             ```
 
             ---
@@ -2913,8 +2895,6 @@ fn main() { let foo_test = name_with_dashes::wrapper::Thing::new$0(); }
 fn hover_field_pat_shorthand_ref_match_ergonomics() {
     check(
         r#"
-//- /main.rs target_data_layout:e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128
-
 struct S {
     f: i32,
 }
@@ -3506,8 +3486,8 @@ impl<const LEN: usize> Foo<LEN$0> {}
 }
 
 #[test]
-fn hover_const_eval_variant() {
-    // show hex for <10
+fn hover_const_eval_discriminant() {
+    // Don't show hex for <10
     check(
         r#"
 #[repr(u8)]
@@ -3532,7 +3512,7 @@ enum E {
             This is a doc
         "#]],
     );
-    // show hex for >10
+    // Show hex for >10
     check(
         r#"
 #[repr(u8)]
@@ -3656,7 +3636,7 @@ trait T {
 }
 impl T for i32 {
     const AA: A = A {
-        i: 2
+        i: 2 + 3
     }
 }
 fn main() {
@@ -3671,9 +3651,7 @@ fn main() {
         ```
 
         ```rust
-        const AA: A = A {
-                i: 2
-            }
+        const AA: A = A { i: 5 }
         ```
     "#]],
     );
@@ -3792,7 +3770,6 @@ const FOO$0: usize = 1 << 3;
             This is a doc
         "#]],
     );
-    // show hex for >10
     check(
         r#"
 /// This is a doc
@@ -3850,7 +3827,7 @@ const FOO$0: i32 = 2 - 3;
             ```
 
             ```rust
-            const FOO: i32 = -1
+            const FOO: i32 = -1 (0xFFFFFFFF)
             ```
 
             ---
@@ -4011,6 +3988,28 @@ const FOO$0: f32 = 1f32;
             This is a doc
         "#]],
     );
+    // Don't show `<ref-not-supported>` in const hover
+    check(
+        r#"
+/// This is a doc
+const FOO$0: &i32 = &2;
+"#,
+        expect![[r#"
+            *FOO*
+
+            ```rust
+            test
+            ```
+
+            ```rust
+            const FOO: &i32 = &2
+            ```
+
+            ---
+
+            This is a doc
+        "#]],
+    );
     //show f64 typecasted from float
     check(
         r#"
@@ -4354,8 +4353,6 @@ fn main() {
 fn hover_intra_doc_links() {
     check(
         r#"
-//- /main.rs target_data_layout:e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128
-
 pub mod theitem {
     /// This is the item. Cool!
     pub struct TheItem;
@@ -4496,7 +4493,7 @@ trait A where
 fn string_shadowed_with_inner_items() {
     check(
         r#"
-//- /main.rs crate:main deps:alloc target_data_layout:e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128
+//- /main.rs crate:main deps:alloc
 
 /// Custom `String` type.
 struct String;
@@ -5191,7 +5188,7 @@ foo_macro!(
             ```
 
             ```rust
-            pub struct Foo
+            pub struct Foo // size = 0, align = 1
             ```
 
             ---
@@ -5205,8 +5202,6 @@ foo_macro!(
 fn hover_intra_in_attr() {
     check(
         r#"
-//- /main.rs target_data_layout:e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128
-
 #[doc = "Doc comment for [`Foo$0`]"]
 pub struct Foo(i32);
 "#,
@@ -5295,7 +5290,7 @@ pub struct Type;
             ```
 
             ```rust
-            const KONST: dep::Type = $crate::Type
+            const KONST: dep::Type = Type
             ```
         "#]],
     );
@@ -5327,8 +5322,6 @@ enum Enum {
 fn hover_record_variant_field() {
     check(
         r#"
-//- /main.rs target_data_layout:e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128
-
 enum Enum {
     RecordV { field$0: u32 }
 }
@@ -5647,3 +5640,204 @@ fn main() {
         "#]],
     );
 }
+
+#[test]
+fn assoc_fn_in_block_local_impl() {
+    check(
+        r#"
+struct S;
+mod m {
+    const _: () = {
+        impl crate::S {
+            pub(crate) fn foo() {}
+        }
+    };
+}
+fn test() {
+    S::foo$0();
+}
+"#,
+        expect![[r#"
+            *foo*
+
+            ```rust
+            test::S
+            ```
+
+            ```rust
+            pub(crate) fn foo()
+            ```
+        "#]],
+    );
+
+    check(
+        r#"
+struct S;
+mod m {
+    const _: () = {
+        const _: () = {
+            impl crate::S {
+                pub(crate) fn foo() {}
+            }
+        };
+    };
+}
+fn test() {
+    S::foo$0();
+}
+"#,
+        expect![[r#"
+            *foo*
+
+            ```rust
+            test::S
+            ```
+
+            ```rust
+            pub(crate) fn foo()
+            ```
+        "#]],
+    );
+
+    check(
+        r#"
+struct S;
+mod m {
+    mod inner {
+        const _: () = {
+            impl crate::S {
+                pub(super) fn foo() {}
+            }
+        };
+    }
+
+    fn test() {
+        crate::S::foo$0();
+    }
+}
+"#,
+        expect![[r#"
+            *foo*
+
+            ```rust
+            test::S
+            ```
+
+            ```rust
+            pub(super) fn foo()
+            ```
+        "#]],
+    );
+}
+
+#[test]
+fn assoc_const_in_block_local_impl() {
+    check(
+        r#"
+struct S;
+mod m {
+    const _: () = {
+        impl crate::S {
+            pub(crate) const A: () = ();
+        }
+    };
+}
+fn test() {
+    S::A$0;
+}
+"#,
+        expect![[r#"
+            *A*
+
+            ```rust
+            test
+            ```
+
+            ```rust
+            pub(crate) const A: () = ()
+            ```
+        "#]],
+    );
+
+    check(
+        r#"
+struct S;
+mod m {
+    const _: () = {
+        const _: () = {
+            impl crate::S {
+                pub(crate) const A: () = ();
+            }
+        };
+    };
+}
+fn test() {
+    S::A$0;
+}
+"#,
+        expect![[r#"
+            *A*
+
+            ```rust
+            test
+            ```
+
+            ```rust
+            pub(crate) const A: () = ()
+            ```
+        "#]],
+    );
+
+    check(
+        r#"
+struct S;
+mod m {
+    mod inner {
+        const _: () = {
+            impl crate::S {
+                pub(super) const A: () = ();
+            }
+        };
+    }
+
+    fn test() {
+        crate::S::A$0;
+    }
+}
+"#,
+        expect![[r#"
+            *A*
+
+            ```rust
+            test
+            ```
+
+            ```rust
+            pub(super) const A: () = ()
+            ```
+        "#]],
+    );
+}
+
+#[test]
+fn field_as_method_call_fallback() {
+    check(
+        r#"
+struct S { f: u32 }
+fn test() {
+    S { f: 0 }.f$0();
+}
+"#,
+        expect![[r#"
+            *f*
+
+            ```rust
+            test::S
+            ```
+
+            ```rust
+            f: u32 // size = 4, align = 4, offset = 0
+            ```
+        "#]],
+    );
+}
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs
index 188eb7f977b..729780fa0c9 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs
@@ -606,14 +606,13 @@ fn a() {
     }
 
     #[test]
-    fn bug() {
+    fn let_stmt_explicit_ty() {
         check_with_config(
             InlayHintsConfig { adjustment_hints: AdjustmentHints::Always, ..DISABLED_CONFIG },
             r#"
 fn main() {
-    // These should be identical, but they are not...
-
     let () = return;
+           //^^^^^^<never-to-any>
     let (): () = return;
                //^^^^^^<never-to-any>
 }
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs
index 4af7f9bdb73..6a50927333d 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs
@@ -176,15 +176,12 @@ fn pat_is_enum_variant(db: &RootDatabase, bind_pat: &ast::IdentPat, pat_ty: &hir
 mod tests {
     // This module also contains tests for super::closure_ret
 
-    use expect_test::expect;
     use syntax::{TextRange, TextSize};
     use test_utils::extract_annotations;
 
     use crate::{fixture, inlay_hints::InlayHintsConfig};
 
-    use crate::inlay_hints::tests::{
-        check, check_expect, check_with_config, DISABLED_CONFIG, TEST_CONFIG,
-    };
+    use crate::inlay_hints::tests::{check, check_with_config, DISABLED_CONFIG, TEST_CONFIG};
     use crate::ClosureReturnTypeHints;
 
     #[track_caller]
@@ -278,8 +275,7 @@ fn main() {
     #[test]
     fn iterator_hint_regression_issue_12674() {
         // Ensure we don't crash while solving the projection type of iterators.
-        check_expect(
-            InlayHintsConfig { chaining_hints: true, ..DISABLED_CONFIG },
+        let (analysis, file_id) = fixture::file(
             r#"
 //- minicore: iterators
 struct S<T>(T);
@@ -302,107 +298,18 @@ impl<'a, T> Iterator for SliceIter<'a, T> {
 
 fn main(a: SliceIter<'_, Container>) {
     a
-    .filter_map(|c| Some(c.elements.iter().filter_map(|v| Some(v))))
-    .map(|e| e);
+        .filter_map(|c| Some(c.elements.iter().filter_map(|v| Some(v))))
+        .map(|e| e);
 }
-            "#,
-            expect![[r#"
-                [
-                    InlayHint {
-                        range: 484..554,
-                        kind: Chaining,
-                        label: [
-                            "impl ",
-                            InlayHintLabelPart {
-                                text: "Iterator",
-                                linked_location: Some(
-                                    FileRange {
-                                        file_id: FileId(
-                                            1,
-                                        ),
-                                        range: 2611..2619,
-                                    },
-                                ),
-                                tooltip: "",
-                            },
-                            "<",
-                            InlayHintLabelPart {
-                                text: "Item",
-                                linked_location: Some(
-                                    FileRange {
-                                        file_id: FileId(
-                                            1,
-                                        ),
-                                        range: 2643..2647,
-                                    },
-                                ),
-                                tooltip: "",
-                            },
-                            " = impl ",
-                            InlayHintLabelPart {
-                                text: "Iterator",
-                                linked_location: Some(
-                                    FileRange {
-                                        file_id: FileId(
-                                            1,
-                                        ),
-                                        range: 2611..2619,
-                                    },
-                                ),
-                                tooltip: "",
-                            },
-                            "<",
-                            InlayHintLabelPart {
-                                text: "Item",
-                                linked_location: Some(
-                                    FileRange {
-                                        file_id: FileId(
-                                            1,
-                                        ),
-                                        range: 2643..2647,
-                                    },
-                                ),
-                                tooltip: "",
-                            },
-                            " = &&str>>",
-                        ],
-                    },
-                    InlayHint {
-                        range: 484..485,
-                        kind: Chaining,
-                        label: [
-                            "",
-                            InlayHintLabelPart {
-                                text: "SliceIter",
-                                linked_location: Some(
-                                    FileRange {
-                                        file_id: FileId(
-                                            0,
-                                        ),
-                                        range: 289..298,
-                                    },
-                                ),
-                                tooltip: "",
-                            },
-                            "<",
-                            InlayHintLabelPart {
-                                text: "Container",
-                                linked_location: Some(
-                                    FileRange {
-                                        file_id: FileId(
-                                            0,
-                                        ),
-                                        range: 238..247,
-                                    },
-                                ),
-                                tooltip: "",
-                            },
-                            ">",
-                        ],
-                    },
-                ]
-            "#]],
+"#,
         );
+        analysis
+            .inlay_hints(
+                &InlayHintsConfig { chaining_hints: true, ..DISABLED_CONFIG },
+                file_id,
+                None,
+            )
+            .unwrap();
     }
 
     #[test]
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs
index 0c54f084c19..0a7513e465a 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs
@@ -435,7 +435,7 @@ fn main() {
                                         file_id: FileId(
                                             1,
                                         ),
-                                        range: 2611..2619,
+                                        range: 3386..3394,
                                     },
                                 ),
                                 tooltip: "",
@@ -448,7 +448,7 @@ fn main() {
                                         file_id: FileId(
                                             1,
                                         ),
-                                        range: 2643..2647,
+                                        range: 3418..3422,
                                     },
                                 ),
                                 tooltip: "",
@@ -468,7 +468,7 @@ fn main() {
                                         file_id: FileId(
                                             1,
                                         ),
-                                        range: 2611..2619,
+                                        range: 3386..3394,
                                     },
                                 ),
                                 tooltip: "",
@@ -481,7 +481,7 @@ fn main() {
                                         file_id: FileId(
                                             1,
                                         ),
-                                        range: 2643..2647,
+                                        range: 3418..3422,
                                     },
                                 ),
                                 tooltip: "",
@@ -501,7 +501,7 @@ fn main() {
                                         file_id: FileId(
                                             1,
                                         ),
-                                        range: 2611..2619,
+                                        range: 3386..3394,
                                     },
                                 ),
                                 tooltip: "",
@@ -514,7 +514,7 @@ fn main() {
                                         file_id: FileId(
                                             1,
                                         ),
-                                        range: 2643..2647,
+                                        range: 3418..3422,
                                     },
                                 ),
                                 tooltip: "",
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs
index 5dd51ad11f4..67eaa553ada 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs
@@ -59,8 +59,14 @@ fn variant_hints(
         },
         kind: InlayKind::Discriminant,
         label: InlayHintLabel::simple(
-            match &d {
-                Ok(v) => format!("{}", v),
+            match d {
+                Ok(x) => {
+                    if x >= 10 {
+                        format!("{x} ({x:#X})")
+                    } else {
+                        format!("{x}")
+                    }
+                }
                 Err(_) => "?".into(),
             },
             Some(InlayTooltip::String(match &d {
diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs
index f2b535bdc7e..078b66dd395 100644
--- a/src/tools/rust-analyzer/crates/ide/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs
@@ -55,6 +55,7 @@ mod syntax_tree;
 mod typing;
 mod view_crate_graph;
 mod view_hir;
+mod view_mir;
 mod view_item_tree;
 mod shuffle_crate_graph;
 
@@ -308,6 +309,10 @@ impl Analysis {
         self.with_db(|db| view_hir::view_hir(db, position))
     }
 
+    pub fn view_mir(&self, position: FilePosition) -> Cancellable<String> {
+        self.with_db(|db| view_mir::view_mir(db, position))
+    }
+
     pub fn view_item_tree(&self, file_id: FileId) -> Cancellable<String> {
         self.with_db(|db| view_item_tree::view_item_tree(db, file_id))
     }
diff --git a/src/tools/rust-analyzer/crates/ide/src/markup.rs b/src/tools/rust-analyzer/crates/ide/src/markup.rs
index de9fef61a78..411eb695fbf 100644
--- a/src/tools/rust-analyzer/crates/ide/src/markup.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/markup.rs
@@ -32,7 +32,7 @@ impl Markup {
     pub fn as_str(&self) -> &str {
         self.text.as_str()
     }
-    pub fn fenced_block(contents: &impl fmt::Display) -> Markup {
+    pub fn fenced_block(contents: impl fmt::Display) -> Markup {
         format!("```rust\n{contents}\n```").into()
     }
 }
diff --git a/src/tools/rust-analyzer/crates/ide/src/moniker.rs b/src/tools/rust-analyzer/crates/ide/src/moniker.rs
index af5e96d2381..349e79ecfdd 100644
--- a/src/tools/rust-analyzer/crates/ide/src/moniker.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/moniker.rs
@@ -208,6 +208,9 @@ pub(crate) fn def_to_moniker(
         Definition::Trait(trait_) => {
             MonikerDescriptor { name: trait_.name(db), desc: MonikerDescriptorKind::Type }
         }
+        Definition::TraitAlias(ta) => {
+            MonikerDescriptor { name: ta.name(db), desc: MonikerDescriptorKind::Type }
+        }
         Definition::TypeAlias(ta) => {
             MonikerDescriptor { name: ta.name(db), desc: MonikerDescriptorKind::TypeParameter }
         }
diff --git a/src/tools/rust-analyzer/crates/ide/src/move_item.rs b/src/tools/rust-analyzer/crates/ide/src/move_item.rs
index ffc4bdd7da3..b955ea99f0c 100644
--- a/src/tools/rust-analyzer/crates/ide/src/move_item.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/move_item.rs
@@ -73,6 +73,7 @@ fn find_ancestors(item: SyntaxElement, direction: Direction, range: TextRange) -
         SyntaxKind::MACRO_CALL,
         SyntaxKind::TYPE_ALIAS,
         SyntaxKind::TRAIT,
+        SyntaxKind::TRAIT_ALIAS,
         SyntaxKind::IMPL,
         SyntaxKind::MACRO_DEF,
         SyntaxKind::STRUCT,
diff --git a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs
index 3aa799d43a8..6aae82f9816 100644
--- a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs
@@ -5,7 +5,7 @@ use std::fmt;
 use either::Either;
 use hir::{
     symbols::FileSymbol, AssocItem, Documentation, FieldSource, HasAttrs, HasSource, HirDisplay,
-    InFile, ModuleSource, Semantics,
+    InFile, LocalSource, ModuleSource, Semantics,
 };
 use ide_db::{
     base_db::{FileId, FileRange},
@@ -192,6 +192,7 @@ impl TryToNav for Definition {
             Definition::Const(it) => it.try_to_nav(db),
             Definition::Static(it) => it.try_to_nav(db),
             Definition::Trait(it) => it.try_to_nav(db),
+            Definition::TraitAlias(it) => it.try_to_nav(db),
             Definition::TypeAlias(it) => it.try_to_nav(db),
             Definition::BuiltinType(_) => None,
             Definition::ToolModule(_) => None,
@@ -212,6 +213,7 @@ impl TryToNav for hir::ModuleDef {
             hir::ModuleDef::Const(it) => it.try_to_nav(db),
             hir::ModuleDef::Static(it) => it.try_to_nav(db),
             hir::ModuleDef::Trait(it) => it.try_to_nav(db),
+            hir::ModuleDef::TraitAlias(it) => it.try_to_nav(db),
             hir::ModuleDef::TypeAlias(it) => it.try_to_nav(db),
             hir::ModuleDef::Macro(it) => it.try_to_nav(db),
             hir::ModuleDef::BuiltinType(_) => None,
@@ -249,6 +251,9 @@ impl ToNavFromAst for hir::TypeAlias {
 impl ToNavFromAst for hir::Trait {
     const KIND: SymbolKind = SymbolKind::Trait;
 }
+impl ToNavFromAst for hir::TraitAlias {
+    const KIND: SymbolKind = SymbolKind::TraitAlias;
+}
 
 impl<D> TryToNav for D
 where
@@ -382,9 +387,11 @@ impl TryToNav for hir::GenericParam {
     }
 }
 
-impl ToNav for hir::Local {
+impl ToNav for LocalSource {
     fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
-        let InFile { file_id, value } = self.source(db);
+        let InFile { file_id, value } = &self.source;
+        let file_id = *file_id;
+        let local = self.local;
         let (node, name) = match &value {
             Either::Left(bind_pat) => (bind_pat.syntax(), bind_pat.name()),
             Either::Right(it) => (it.syntax(), it.name()),
@@ -393,10 +400,10 @@ impl ToNav for hir::Local {
         let FileRange { file_id, range: full_range } =
             InFile::new(file_id, node).original_file_range(db);
 
-        let name = self.name(db).to_smol_str();
-        let kind = if self.is_self(db) {
+        let name = local.name(db).to_smol_str();
+        let kind = if local.is_self(db) {
             SymbolKind::SelfParam
-        } else if self.is_param(db) {
+        } else if local.is_param(db) {
             SymbolKind::ValueParam
         } else {
             SymbolKind::Local
@@ -414,6 +421,12 @@ impl ToNav for hir::Local {
     }
 }
 
+impl ToNav for hir::Local {
+    fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
+        self.primary_source(db).to_nav(db)
+    }
+}
+
 impl ToNav for hir::Label {
     fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
         let InFile { file_id, value } = self.source(db);
@@ -544,6 +557,7 @@ pub(crate) fn description_from_symbol(db: &RootDatabase, symbol: &FileSymbol) ->
             ast::Struct(it) => sema.to_def(&it).map(|it| it.display(db).to_string()),
             ast::Enum(it) => sema.to_def(&it).map(|it| it.display(db).to_string()),
             ast::Trait(it) => sema.to_def(&it).map(|it| it.display(db).to_string()),
+            ast::TraitAlias(it) => sema.to_def(&it).map(|it| it.display(db).to_string()),
             ast::Module(it) => sema.to_def(&it).map(|it| it.display(db).to_string()),
             ast::TypeAlias(it) => sema.to_def(&it).map(|it| it.display(db).to_string()),
             ast::Const(it) => sema.to_def(&it).map(|it| it.display(db).to_string()),
diff --git a/src/tools/rust-analyzer/crates/ide/src/references.rs b/src/tools/rust-analyzer/crates/ide/src/references.rs
index cabbc287279..3684c1033f3 100644
--- a/src/tools/rust-analyzer/crates/ide/src/references.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/references.rs
@@ -1356,6 +1356,38 @@ impl Foo {
     }
 
     #[test]
+    fn test_trait_alias() {
+        check(
+            r#"
+trait Foo {}
+trait Bar$0 = Foo where Self: ;
+fn foo<T: Bar>(_: impl Bar, _: &dyn Bar) {}
+"#,
+            expect![[r#"
+                Bar TraitAlias FileId(0) 13..42 19..22
+
+                FileId(0) 53..56
+                FileId(0) 66..69
+                FileId(0) 79..82
+            "#]],
+        );
+    }
+
+    #[test]
+    fn test_trait_alias_self() {
+        check(
+            r#"
+trait Foo = where Self$0: ;
+"#,
+            expect![[r#"
+                Self TypeParam FileId(0) 6..9 6..9
+
+                FileId(0) 18..22
+            "#]],
+        );
+    }
+
+    #[test]
     fn test_attr_differs_from_fn_with_same_name() {
         check(
             r#"
diff --git a/src/tools/rust-analyzer/crates/ide/src/rename.rs b/src/tools/rust-analyzer/crates/ide/src/rename.rs
index c0237e1edd0..e10c4638102 100644
--- a/src/tools/rust-analyzer/crates/ide/src/rename.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/rename.rs
@@ -353,6 +353,11 @@ mod tests {
     fn check(new_name: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
         let ra_fixture_after = &trim_indent(ra_fixture_after);
         let (analysis, position) = fixture::position(ra_fixture_before);
+        if !ra_fixture_after.starts_with("error: ") {
+            if let Err(err) = analysis.prepare_rename(position).unwrap() {
+                panic!("Prepare rename to '{new_name}' was failed: {err}")
+            }
+        }
         let rename_result = analysis
             .rename(position, new_name)
             .unwrap_or_else(|err| panic!("Rename to '{new_name}' was cancelled: {err}"));
@@ -1710,6 +1715,23 @@ fn foo(bar: i32) -> Foo {
     }
 
     #[test]
+    fn test_rename_local_simple() {
+        check(
+            "i",
+            r#"
+fn foo(bar$0: i32) -> i32 {
+    bar
+}
+"#,
+            r#"
+fn foo(i: i32) -> i32 {
+    i
+}
+"#,
+        );
+    }
+
+    #[test]
     fn test_rename_local_put_init_shorthand() {
         cov_mark::check!(test_rename_local_put_init_shorthand);
         check(
diff --git a/src/tools/rust-analyzer/crates/ide/src/runnables.rs b/src/tools/rust-analyzer/crates/ide/src/runnables.rs
index 5b35262aabe..8a8a9151c42 100644
--- a/src/tools/rust-analyzer/crates/ide/src/runnables.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/runnables.rs
@@ -2,7 +2,7 @@ use std::fmt;
 
 use ast::HasName;
 use cfg::CfgExpr;
-use hir::{AsAssocItem, HasAttrs, HasSource, HirDisplay, Semantics};
+use hir::{AsAssocItem, HasAttrs, HasSource, Semantics};
 use ide_assists::utils::test_related_attribute;
 use ide_db::{
     base_db::{FilePosition, FileRange},
@@ -195,14 +195,13 @@ pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> {
 //
 // Provides a sneak peek of all tests where the current item is used.
 //
-// The simplest way to use this feature is via the context menu:
-//  - Right-click on the selected item. The context menu opens.
-//  - Select **Peek related tests**
+// The simplest way to use this feature is via the context menu. Right-click on
+// the selected item. The context menu opens. Select **Peek Related Tests**.
 //
 // |===
 // | Editor  | Action Name
 //
-// | VS Code | **rust-analyzer: Peek related tests**
+// | VS Code | **rust-analyzer: Peek Related Tests**
 // |===
 pub(crate) fn related_tests(
     db: &RootDatabase,
@@ -371,9 +370,9 @@ pub(crate) fn runnable_impl(
     let nav = def.try_to_nav(sema.db)?;
     let ty = def.self_ty(sema.db);
     let adt_name = ty.as_adt()?.name(sema.db);
-    let mut ty_args = ty.type_arguments().peekable();
+    let mut ty_args = ty.generic_parameters(sema.db).peekable();
     let params = if ty_args.peek().is_some() {
-        format!("<{}>", ty_args.format_with(",", |ty, cb| cb(&ty.display(sema.db))))
+        format!("<{}>", ty_args.format_with(",", |ty, cb| cb(&ty)))
     } else {
         String::new()
     };
@@ -417,6 +416,7 @@ fn module_def_doctest(db: &RootDatabase, def: Definition) -> Option<Runnable> {
         Definition::Const(it) => it.attrs(db),
         Definition::Static(it) => it.attrs(db),
         Definition::Trait(it) => it.attrs(db),
+        Definition::TraitAlias(it) => it.attrs(db),
         Definition::TypeAlias(it) => it.attrs(db),
         Definition::Macro(it) => it.attrs(db),
         Definition::SelfType(it) => it.attrs(db),
@@ -437,14 +437,10 @@ fn module_def_doctest(db: &RootDatabase, def: Definition) -> Option<Runnable> {
                 let ty = imp.self_ty(db);
                 if let Some(adt) = ty.as_adt() {
                     let name = adt.name(db);
-                    let mut ty_args = ty.type_arguments().peekable();
+                    let mut ty_args = ty.generic_parameters(db).peekable();
                     format_to!(path, "{}", name);
                     if ty_args.peek().is_some() {
-                        format_to!(
-                            path,
-                            "<{}>",
-                            ty_args.format_with(",", |ty, cb| cb(&ty.display(db)))
-                        );
+                        format_to!(path, "<{}>", ty_args.format_with(",", |ty, cb| cb(&ty)));
                     }
                     format_to!(path, "::{}", def_name);
                     path.retain(|c| c != ' ');
@@ -1001,6 +997,221 @@ impl Data {
     }
 
     #[test]
+    fn test_runnables_doc_test_in_impl_with_lifetime() {
+        check(
+            r#"
+//- /lib.rs
+$0
+fn main() {}
+
+struct Data<'a>;
+impl Data<'a> {
+    /// ```
+    /// let x = 5;
+    /// ```
+    fn foo() {}
+}
+"#,
+            &[Bin, DocTest],
+            expect![[r#"
+                [
+                    Runnable {
+                        use_name_in_title: false,
+                        nav: NavigationTarget {
+                            file_id: FileId(
+                                0,
+                            ),
+                            full_range: 1..13,
+                            focus_range: 4..8,
+                            name: "main",
+                            kind: Function,
+                        },
+                        kind: Bin,
+                        cfg: None,
+                    },
+                    Runnable {
+                        use_name_in_title: false,
+                        nav: NavigationTarget {
+                            file_id: FileId(
+                                0,
+                            ),
+                            full_range: 52..106,
+                            name: "foo",
+                        },
+                        kind: DocTest {
+                            test_id: Path(
+                                "Data<'a>::foo",
+                            ),
+                        },
+                        cfg: None,
+                    },
+                ]
+            "#]],
+        );
+    }
+
+    #[test]
+    fn test_runnables_doc_test_in_impl_with_lifetime_and_types() {
+        check(
+            r#"
+//- /lib.rs
+$0
+fn main() {}
+
+struct Data<'a, T, U>;
+impl<T, U> Data<'a, T, U> {
+    /// ```
+    /// let x = 5;
+    /// ```
+    fn foo() {}
+}
+"#,
+            &[Bin, DocTest],
+            expect![[r#"
+                [
+                    Runnable {
+                        use_name_in_title: false,
+                        nav: NavigationTarget {
+                            file_id: FileId(
+                                0,
+                            ),
+                            full_range: 1..13,
+                            focus_range: 4..8,
+                            name: "main",
+                            kind: Function,
+                        },
+                        kind: Bin,
+                        cfg: None,
+                    },
+                    Runnable {
+                        use_name_in_title: false,
+                        nav: NavigationTarget {
+                            file_id: FileId(
+                                0,
+                            ),
+                            full_range: 70..124,
+                            name: "foo",
+                        },
+                        kind: DocTest {
+                            test_id: Path(
+                                "Data<'a,T,U>::foo",
+                            ),
+                        },
+                        cfg: None,
+                    },
+                ]
+            "#]],
+        );
+    }
+
+    #[test]
+    fn test_runnables_doc_test_in_impl_with_const() {
+        check(
+            r#"
+//- /lib.rs
+$0
+fn main() {}
+
+struct Data<const N: usize>;
+impl<const N: usize> Data<N> {
+    /// ```
+    /// let x = 5;
+    /// ```
+    fn foo() {}
+}
+"#,
+            &[Bin, DocTest],
+            expect![[r#"
+                [
+                    Runnable {
+                        use_name_in_title: false,
+                        nav: NavigationTarget {
+                            file_id: FileId(
+                                0,
+                            ),
+                            full_range: 1..13,
+                            focus_range: 4..8,
+                            name: "main",
+                            kind: Function,
+                        },
+                        kind: Bin,
+                        cfg: None,
+                    },
+                    Runnable {
+                        use_name_in_title: false,
+                        nav: NavigationTarget {
+                            file_id: FileId(
+                                0,
+                            ),
+                            full_range: 79..133,
+                            name: "foo",
+                        },
+                        kind: DocTest {
+                            test_id: Path(
+                                "Data<N>::foo",
+                            ),
+                        },
+                        cfg: None,
+                    },
+                ]
+            "#]],
+        );
+    }
+
+    #[test]
+    fn test_runnables_doc_test_in_impl_with_lifetime_types_and_const() {
+        check(
+            r#"
+//- /lib.rs
+$0
+fn main() {}
+
+struct Data<'a, T, const N: usize>;
+impl<'a, T, const N: usize> Data<'a, T, N> {
+    /// ```
+    /// let x = 5;
+    /// ```
+    fn foo() {}
+}
+"#,
+            &[Bin, DocTest],
+            expect![[r#"
+                [
+                    Runnable {
+                        use_name_in_title: false,
+                        nav: NavigationTarget {
+                            file_id: FileId(
+                                0,
+                            ),
+                            full_range: 1..13,
+                            focus_range: 4..8,
+                            name: "main",
+                            kind: Function,
+                        },
+                        kind: Bin,
+                        cfg: None,
+                    },
+                    Runnable {
+                        use_name_in_title: false,
+                        nav: NavigationTarget {
+                            file_id: FileId(
+                                0,
+                            ),
+                            full_range: 100..154,
+                            name: "foo",
+                        },
+                        kind: DocTest {
+                            test_id: Path(
+                                "Data<'a,T,N>::foo",
+                            ),
+                        },
+                        cfg: None,
+                    },
+                ]
+            "#]],
+        );
+    }
+    #[test]
     fn test_runnables_module() {
         check(
             r#"
@@ -2063,6 +2274,59 @@ mod tests {
     }
 
     #[test]
+    fn test_runnables_doc_test_in_impl_with_lifetime_type_const_value() {
+        check(
+            r#"
+//- /lib.rs
+$0
+fn main() {}
+
+struct Data<'a, A, const B: usize, C, const D: u32>;
+impl<A, C, const D: u32> Data<'a, A, 12, C, D> {
+    /// ```
+    /// ```
+    fn foo() {}
+}
+"#,
+            &[Bin, DocTest],
+            expect![[r#"
+                [
+                    Runnable {
+                        use_name_in_title: false,
+                        nav: NavigationTarget {
+                            file_id: FileId(
+                                0,
+                            ),
+                            full_range: 1..13,
+                            focus_range: 4..8,
+                            name: "main",
+                            kind: Function,
+                        },
+                        kind: Bin,
+                        cfg: None,
+                    },
+                    Runnable {
+                        use_name_in_title: false,
+                        nav: NavigationTarget {
+                            file_id: FileId(
+                                0,
+                            ),
+                            full_range: 121..156,
+                            name: "foo",
+                        },
+                        kind: DocTest {
+                            test_id: Path(
+                                "Data<'a,A,12,C,D>::foo",
+                            ),
+                        },
+                        cfg: None,
+                    },
+                ]
+            "#]],
+        );
+    }
+
+    #[test]
     fn doc_test_type_params() {
         check(
             r#"
diff --git a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs
index f70ca55a508..2c08c457b33 100644
--- a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs
@@ -172,7 +172,7 @@ fn signature_help_for_call(
 
     res.signature.push('(');
     {
-        if let Some(self_param) = callable.receiver_param(db) {
+        if let Some((self_param, _)) = callable.receiver_param(db) {
             format_to!(res.signature, "{}", self_param)
         }
         let mut buf = String::new();
@@ -252,6 +252,10 @@ fn signature_help_for_generics(
             res.doc = it.docs(db).map(|it| it.into());
             format_to!(res.signature, "trait {}", it.name(db));
         }
+        hir::GenericDef::TraitAlias(it) => {
+            res.doc = it.docs(db).map(|it| it.into());
+            format_to!(res.signature, "trait {}", it.name(db));
+        }
         hir::GenericDef::TypeAlias(it) => {
             res.doc = it.docs(db).map(|it| it.into());
             format_to!(res.signature, "type {}", it.name(db));
diff --git a/src/tools/rust-analyzer/crates/ide/src/static_index.rs b/src/tools/rust-analyzer/crates/ide/src/static_index.rs
index 3f7f6885f61..c97691b14a5 100644
--- a/src/tools/rust-analyzer/crates/ide/src/static_index.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/static_index.rs
@@ -139,6 +139,7 @@ impl StaticIndex<'_> {
             documentation: true,
             keywords: true,
             format: crate::HoverDocFormat::Markdown,
+            interpret_tests: false,
         };
         let tokens = tokens.filter(|token| {
             matches!(
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs
index 892e6a9bb0a..2111baad74d 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs
@@ -217,7 +217,9 @@ fn highlight_name_ref(
         // to anything when used.
         // We can fix this for derive attributes since derive helpers are recorded, but not for
         // general attributes.
-        None if name_ref.syntax().ancestors().any(|it| it.kind() == ATTR) => {
+        None if name_ref.syntax().ancestors().any(|it| it.kind() == ATTR)
+            && !sema.hir_file_for(name_ref.syntax()).is_derive_attr_pseudo_expansion(sema.db) =>
+        {
             return HlTag::Symbol(SymbolKind::Attribute).into();
         }
         None => return HlTag::UnresolvedReference.into(),
@@ -410,6 +412,7 @@ fn highlight_def(
             h
         }
         Definition::Trait(_) => Highlight::new(HlTag::Symbol(SymbolKind::Trait)),
+        Definition::TraitAlias(_) => Highlight::new(HlTag::Symbol(SymbolKind::TraitAlias)),
         Definition::TypeAlias(type_) => {
             let mut h = Highlight::new(HlTag::Symbol(SymbolKind::TypeAlias));
 
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs
index 9139528c7ed..3c4cfc78152 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs
@@ -274,6 +274,7 @@ fn module_def_to_hl_tag(def: Definition) -> HlTag {
         Definition::Const(_) => SymbolKind::Const,
         Definition::Static(_) => SymbolKind::Static,
         Definition::Trait(_) => SymbolKind::Trait,
+        Definition::TraitAlias(_) => SymbolKind::TraitAlias,
         Definition::TypeAlias(_) => SymbolKind::TypeAlias,
         Definition::BuiltinType(_) => return HlTag::BuiltinType,
         Definition::Macro(_) => SymbolKind::Macro,
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs
index 3949f1189bd..a81c4ee0cbd 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs
@@ -150,6 +150,7 @@ impl HlTag {
                 SymbolKind::Struct => "struct",
                 SymbolKind::ToolModule => "tool_module",
                 SymbolKind::Trait => "trait",
+                SymbolKind::TraitAlias => "trait_alias",
                 SymbolKind::TypeAlias => "type_alias",
                 SymbolKind::TypeParam => "type_param",
                 SymbolKind::Union => "union",
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html
index 1a439881476..567ab8ccc11 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html
@@ -53,6 +53,6 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 <span class="comment">// This is another normal comment</span>
 <span class="comment documentation">/// This is another doc comment</span>
 <span class="comment">// This is another normal comment</span>
-<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="attribute attribute default_library library">derive</span><span class="parenthesis attribute">(</span><span class="derive attribute default_library library">Copy</span><span class="parenthesis attribute">)</span><span class="attribute_bracket attribute">]</span>
+<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="attribute attribute default_library library">derive</span><span class="parenthesis attribute">(</span><span class="derive attribute default_library library">Copy</span><span class="comma attribute">,</span> <span class="unresolved_reference attribute">Unresolved</span><span class="parenthesis attribute">)</span><span class="attribute_bracket attribute">]</span>
 <span class="comment">// The reason for these being here is to test AttrIds</span>
 <span class="keyword">struct</span> <span class="struct declaration">Foo</span><span class="semicolon">;</span></code></pre>
\ No newline at end of file
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs
index fc9b5d3ba4c..ac9bd8e39dc 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs
@@ -34,7 +34,7 @@ fn attributes() {
 // This is another normal comment
 /// This is another doc comment
 // This is another normal comment
-#[derive(Copy)]
+#[derive(Copy, Unresolved)]
 // The reason for these being here is to test AttrIds
 struct Foo;
 "#,
diff --git a/src/tools/rust-analyzer/crates/ide/src/view_mir.rs b/src/tools/rust-analyzer/crates/ide/src/view_mir.rs
new file mode 100644
index 00000000000..a36aba58bc0
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide/src/view_mir.rs
@@ -0,0 +1,29 @@
+use hir::{DefWithBody, Semantics};
+use ide_db::base_db::FilePosition;
+use ide_db::RootDatabase;
+use syntax::{algo::find_node_at_offset, ast, AstNode};
+
+// Feature: View Mir
+//
+// |===
+// | Editor  | Action Name
+//
+// | VS Code | **rust-analyzer: View Mir**
+// |===
+pub(crate) fn view_mir(db: &RootDatabase, position: FilePosition) -> String {
+    body_mir(db, position).unwrap_or_else(|| "Not inside a function body".to_string())
+}
+
+fn body_mir(db: &RootDatabase, position: FilePosition) -> Option<String> {
+    let sema = Semantics::new(db);
+    let source_file = sema.parse(position.file_id);
+
+    let item = find_node_at_offset::<ast::Item>(source_file.syntax(), position.offset)?;
+    let def: DefWithBody = match item {
+        ast::Item::Fn(it) => sema.to_def(&it)?.into(),
+        ast::Item::Const(it) => sema.to_def(&it)?.into(),
+        ast::Item::Static(it) => sema.to_def(&it)?.into(),
+        _ => return None,
+    };
+    Some(def.debug_mir(db))
+}
diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar.rs b/src/tools/rust-analyzer/crates/parser/src/grammar.rs
index 15ec9e167e0..15435a26cea 100644
--- a/src/tools/rust-analyzer/crates/parser/src/grammar.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/grammar.rs
@@ -198,6 +198,10 @@ impl BlockLike {
     fn is_block(self) -> bool {
         self == BlockLike::Block
     }
+
+    fn is_blocklike(kind: SyntaxKind) -> bool {
+        matches!(kind, BLOCK_EXPR | IF_EXPR | WHILE_EXPR | FOR_EXPR | LOOP_EXPR | MATCH_EXPR)
+    }
 }
 
 const VISIBILITY_FIRST: TokenSet = TokenSet::new(&[T![pub], T![crate]]);
diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/attributes.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/attributes.rs
index 4ecaa6e6a85..c13a1943792 100644
--- a/src/tools/rust-analyzer/crates/parser/src/grammar/attributes.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/grammar/attributes.rs
@@ -43,7 +43,7 @@ pub(super) fn meta(p: &mut Parser<'_>) {
     match p.current() {
         T![=] => {
             p.bump(T![=]);
-            if !expressions::expr(p) {
+            if expressions::expr(p).is_none() {
                 p.error("expected expression");
             }
         }
diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs
index 4b080102a2c..a884d8b6ec8 100644
--- a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs
@@ -16,9 +16,9 @@ pub(super) enum Semicolon {
 
 const EXPR_FIRST: TokenSet = LHS_FIRST;
 
-pub(super) fn expr(p: &mut Parser<'_>) -> bool {
+pub(super) fn expr(p: &mut Parser<'_>) -> Option<CompletedMarker> {
     let r = Restrictions { forbid_structs: false, prefer_stmt: false };
-    expr_bp(p, None, r, 1).is_some()
+    expr_bp(p, None, r, 1).map(|(m, _)| m)
 }
 
 pub(super) fn expr_stmt(
@@ -120,16 +120,27 @@ pub(super) fn stmt(p: &mut Parser<'_>, semicolon: Semicolon) {
             // fn f() { let x: i32; }
             types::ascription(p);
         }
+
+        let mut expr_after_eq: Option<CompletedMarker> = None;
         if p.eat(T![=]) {
             // test let_stmt_init
             // fn f() { let x = 92; }
-            expressions::expr(p);
+            expr_after_eq = expressions::expr(p);
         }
 
         if p.at(T![else]) {
+            // test_err let_else_right_curly_brace
+            // fn func() { let Some(_) = {Some(1)} else { panic!("h") };}
+            if let Some(expr) = expr_after_eq {
+                if BlockLike::is_blocklike(expr.kind()) {
+                    p.error(
+                        "right curly brace `}` before `else` in a `let...else` statement not allowed",
+                    )
+                }
+            }
+
             // test let_else
             // fn f() { let Some(x) = opt else { return }; }
-
             let m = p.start();
             p.bump(T![else]);
             block_expr(p);
@@ -578,7 +589,14 @@ fn arg_list(p: &mut Parser<'_>) {
     // fn main() {
     //     foo(#[attr] 92)
     // }
-    delimited(p, T!['('], T![')'], T![,], EXPR_FIRST.union(ATTRIBUTE_FIRST), expr);
+    delimited(
+        p,
+        T!['('],
+        T![')'],
+        T![,],
+        EXPR_FIRST.union(ATTRIBUTE_FIRST),
+        |p: &mut Parser<'_>| expr(p).is_some(),
+    );
     m.complete(p, ARG_LIST);
 }
 
diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs
index efc2603835e..d051dd2682f 100644
--- a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs
@@ -163,10 +163,8 @@ pub(super) fn atom_expr(
             return None;
         }
     };
-    let blocklike = match done.kind() {
-        IF_EXPR | WHILE_EXPR | FOR_EXPR | LOOP_EXPR | MATCH_EXPR | BLOCK_EXPR => BlockLike::Block,
-        _ => BlockLike::NotBlock,
-    };
+    let blocklike =
+        if BlockLike::is_blocklike(done.kind()) { BlockLike::Block } else { BlockLike::NotBlock };
     Some((done, blocklike))
 }
 
@@ -188,7 +186,7 @@ fn tuple_expr(p: &mut Parser<'_>) -> CompletedMarker {
 
         // test tuple_attrs
         // const A: (i64, i64) = (1, #[cfg(test)] 2);
-        if !expr(p) {
+        if expr(p).is_none() {
             break;
         }
 
@@ -221,7 +219,7 @@ fn array_expr(p: &mut Parser<'_>) -> CompletedMarker {
 
         // test array_attrs
         // const A: &[i64] = &[1, #[cfg(test)] 2];
-        if !expr(p) {
+        if expr(p).is_none() {
             break;
         }
 
diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/items/traits.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/items/traits.rs
index c982e2d564c..a8a1ccb15e6 100644
--- a/src/tools/rust-analyzer/crates/parser/src/grammar/items/traits.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/grammar/items/traits.rs
@@ -20,7 +20,7 @@ pub(super) fn trait_(p: &mut Parser<'_>, m: Marker) {
         // trait Z<U> = where Self: T<U>;
         generic_params::opt_where_clause(p);
         p.expect(T![;]);
-        m.complete(p, TRAIT);
+        m.complete(p, TRAIT_ALIAS);
         return;
     }
 
diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/paths.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/paths.rs
index 1064ae9970c..26490aa97e0 100644
--- a/src/tools/rust-analyzer/crates/parser/src/grammar/paths.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/grammar/paths.rs
@@ -77,6 +77,9 @@ fn path_segment(p: &mut Parser<'_>, mode: Mode, first: bool) {
     // type X = <A as B>::Output;
     // fn foo() { <usize as Default>::default(); }
     if first && p.eat(T![<]) {
+        // test_err angled_path_without_qual
+        // type X = <()>;
+        // type Y = <A as B>;
         types::type_(p);
         if p.eat(T![as]) {
             if is_use_path_start(p) {
@@ -86,6 +89,9 @@ fn path_segment(p: &mut Parser<'_>, mode: Mode, first: bool) {
             }
         }
         p.expect(T![>]);
+        if !p.at(T![::]) {
+            p.error("expected `::`");
+        }
     } else {
         let empty = if first {
             p.eat(T![::]);
diff --git a/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs b/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs
index 52b3fc23d59..cd87b304a2f 100644
--- a/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs
@@ -135,6 +135,7 @@ pub enum SyntaxKind {
     STATIC,
     CONST,
     TRAIT,
+    TRAIT_ALIAS,
     IMPL,
     TYPE_ALIAS,
     MACRO_CALL,
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0049_let_else_right_curly_brace_for.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0049_let_else_right_curly_brace_for.rast
new file mode 100644
index 00000000000..026fecf4c9d
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0049_let_else_right_curly_brace_for.rast
@@ -0,0 +1,58 @@
+SOURCE_FILE
+  FN
+    FN_KW "fn"
+    WHITESPACE " "
+    NAME
+      IDENT "f"
+    PARAM_LIST
+      L_PAREN "("
+      R_PAREN ")"
+    WHITESPACE " "
+    BLOCK_EXPR
+      STMT_LIST
+        L_CURLY "{"
+        WHITESPACE "\n    "
+        LET_STMT
+          LET_KW "let"
+          WHITESPACE " "
+          WILDCARD_PAT
+            UNDERSCORE "_"
+          WHITESPACE " "
+          EQ "="
+          WHITESPACE " "
+          FOR_EXPR
+            FOR_KW "for"
+            WHITESPACE " "
+            WILDCARD_PAT
+              UNDERSCORE "_"
+            WHITESPACE " "
+            IN_KW "in"
+            WHITESPACE " "
+            RANGE_EXPR
+              LITERAL
+                INT_NUMBER "0"
+              DOT2 ".."
+              LITERAL
+                INT_NUMBER "10"
+            WHITESPACE " "
+            BLOCK_EXPR
+              STMT_LIST
+                L_CURLY "{"
+                WHITESPACE "\n    "
+                R_CURLY "}"
+          WHITESPACE " "
+          LET_ELSE
+            ELSE_KW "else"
+            WHITESPACE " "
+            BLOCK_EXPR
+              STMT_LIST
+                L_CURLY "{"
+                WHITESPACE "\n        "
+                RETURN_EXPR
+                  RETURN_KW "return"
+                WHITESPACE "\n    "
+                R_CURLY "}"
+          SEMICOLON ";"
+        WHITESPACE "\n"
+        R_CURLY "}"
+error 43: right curly brace `}` before `else` in a `let...else` statement not allowed
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0049_let_else_right_curly_brace_for.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0049_let_else_right_curly_brace_for.rs
new file mode 100644
index 00000000000..d410274198d
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0049_let_else_right_curly_brace_for.rs
@@ -0,0 +1,6 @@
+fn f() {
+    let _ = for _ in 0..10 {
+    } else {
+        return
+    };
+}
\ No newline at end of file
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0050_let_else_right_curly_brace_loop.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0050_let_else_right_curly_brace_loop.rast
new file mode 100644
index 00000000000..10232195413
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0050_let_else_right_curly_brace_loop.rast
@@ -0,0 +1,46 @@
+SOURCE_FILE
+  FN
+    FN_KW "fn"
+    WHITESPACE " "
+    NAME
+      IDENT "f"
+    PARAM_LIST
+      L_PAREN "("
+      R_PAREN ")"
+    WHITESPACE " "
+    BLOCK_EXPR
+      STMT_LIST
+        L_CURLY "{"
+        WHITESPACE "\n    "
+        LET_STMT
+          LET_KW "let"
+          WHITESPACE " "
+          WILDCARD_PAT
+            UNDERSCORE "_"
+          WHITESPACE " "
+          EQ "="
+          WHITESPACE " "
+          LOOP_EXPR
+            LOOP_KW "loop"
+            WHITESPACE " "
+            BLOCK_EXPR
+              STMT_LIST
+                L_CURLY "{"
+                WHITESPACE "\n    "
+                R_CURLY "}"
+          WHITESPACE " "
+          LET_ELSE
+            ELSE_KW "else"
+            WHITESPACE " "
+            BLOCK_EXPR
+              STMT_LIST
+                L_CURLY "{"
+                WHITESPACE "\n        "
+                RETURN_EXPR
+                  RETURN_KW "return"
+                WHITESPACE "\n    "
+                R_CURLY "}"
+          SEMICOLON ";"
+        WHITESPACE "\n"
+        R_CURLY "}"
+error 33: right curly brace `}` before `else` in a `let...else` statement not allowed
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0050_let_else_right_curly_brace_loop.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0050_let_else_right_curly_brace_loop.rs
new file mode 100644
index 00000000000..28b892869a1
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0050_let_else_right_curly_brace_loop.rs
@@ -0,0 +1,6 @@
+fn f() {
+    let _ = loop {
+    } else {
+        return
+    };
+}
\ No newline at end of file
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0051_let_else_right_curly_brace_match.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0051_let_else_right_curly_brace_match.rast
new file mode 100644
index 00000000000..6e1181246c0
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0051_let_else_right_curly_brace_match.rast
@@ -0,0 +1,85 @@
+SOURCE_FILE
+  FN
+    FN_KW "fn"
+    WHITESPACE " "
+    NAME
+      IDENT "f"
+    PARAM_LIST
+      L_PAREN "("
+      R_PAREN ")"
+    WHITESPACE " "
+    BLOCK_EXPR
+      STMT_LIST
+        L_CURLY "{"
+        WHITESPACE "\n    "
+        LET_STMT
+          LET_KW "let"
+          WHITESPACE " "
+          WILDCARD_PAT
+            UNDERSCORE "_"
+          WHITESPACE " "
+          EQ "="
+          WHITESPACE " "
+          MATCH_EXPR
+            MATCH_KW "match"
+            WHITESPACE " "
+            CALL_EXPR
+              PATH_EXPR
+                PATH
+                  PATH_SEGMENT
+                    NAME_REF
+                      IDENT "Some"
+              ARG_LIST
+                L_PAREN "("
+                LITERAL
+                  INT_NUMBER "1"
+                R_PAREN ")"
+            WHITESPACE " "
+            MATCH_ARM_LIST
+              L_CURLY "{"
+              WHITESPACE "\n        "
+              MATCH_ARM
+                TUPLE_STRUCT_PAT
+                  PATH
+                    PATH_SEGMENT
+                      NAME_REF
+                        IDENT "Some"
+                  L_PAREN "("
+                  WILDCARD_PAT
+                    UNDERSCORE "_"
+                  R_PAREN ")"
+                WHITESPACE " "
+                FAT_ARROW "=>"
+                WHITESPACE " "
+                LITERAL
+                  INT_NUMBER "1"
+                COMMA ","
+              WHITESPACE "\n        "
+              MATCH_ARM
+                IDENT_PAT
+                  NAME
+                    IDENT "None"
+                WHITESPACE " "
+                FAT_ARROW "=>"
+                WHITESPACE " "
+                LITERAL
+                  INT_NUMBER "2"
+                COMMA ","
+              WHITESPACE "\n    "
+              R_CURLY "}"
+          WHITESPACE " "
+          LET_ELSE
+            ELSE_KW "else"
+            WHITESPACE " "
+            BLOCK_EXPR
+              STMT_LIST
+                L_CURLY "{"
+                WHITESPACE "\n        "
+                RETURN_EXPR
+                  RETURN_KW "return"
+                WHITESPACE "\n    "
+                R_CURLY "}"
+          SEMICOLON ";"
+        WHITESPACE "\n"
+        R_CURLY "}"
+error 83: right curly brace `}` before `else` in a `let...else` statement not allowed
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0051_let_else_right_curly_brace_match.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0051_let_else_right_curly_brace_match.rs
new file mode 100644
index 00000000000..902d70daed5
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0051_let_else_right_curly_brace_match.rs
@@ -0,0 +1,8 @@
+fn f() {
+    let _ = match Some(1) {
+        Some(_) => 1,
+        None => 2,
+    } else {
+        return
+    };
+}
\ No newline at end of file
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0052_let_else_right_curly_brace_while.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0052_let_else_right_curly_brace_while.rast
new file mode 100644
index 00000000000..298d47d5394
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0052_let_else_right_curly_brace_while.rast
@@ -0,0 +1,49 @@
+SOURCE_FILE
+  FN
+    FN_KW "fn"
+    WHITESPACE " "
+    NAME
+      IDENT "f"
+    PARAM_LIST
+      L_PAREN "("
+      R_PAREN ")"
+    WHITESPACE " "
+    BLOCK_EXPR
+      STMT_LIST
+        L_CURLY "{"
+        WHITESPACE "\n    "
+        LET_STMT
+          LET_KW "let"
+          WHITESPACE " "
+          WILDCARD_PAT
+            UNDERSCORE "_"
+          WHITESPACE " "
+          EQ "="
+          WHITESPACE " "
+          WHILE_EXPR
+            WHILE_KW "while"
+            WHITESPACE " "
+            LITERAL
+              TRUE_KW "true"
+            WHITESPACE " "
+            BLOCK_EXPR
+              STMT_LIST
+                L_CURLY "{"
+                WHITESPACE "\n    "
+                R_CURLY "}"
+          WHITESPACE " "
+          LET_ELSE
+            ELSE_KW "else"
+            WHITESPACE " "
+            BLOCK_EXPR
+              STMT_LIST
+                L_CURLY "{"
+                WHITESPACE "\n        "
+                RETURN_EXPR
+                  RETURN_KW "return"
+                WHITESPACE "\n    "
+                R_CURLY "}"
+          SEMICOLON ";"
+        WHITESPACE "\n"
+        R_CURLY "}"
+error 39: right curly brace `}` before `else` in a `let...else` statement not allowed
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0052_let_else_right_curly_brace_while.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0052_let_else_right_curly_brace_while.rs
new file mode 100644
index 00000000000..a52343d8e6f
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0052_let_else_right_curly_brace_while.rs
@@ -0,0 +1,6 @@
+fn f() {
+    let _ = while true {
+    } else {
+        return
+    };
+}
\ No newline at end of file
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0053_let_else_right_curly_brace_if.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0053_let_else_right_curly_brace_if.rast
new file mode 100644
index 00000000000..c0a4b0400d8
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0053_let_else_right_curly_brace_if.rast
@@ -0,0 +1,57 @@
+SOURCE_FILE
+  FN
+    FN_KW "fn"
+    WHITESPACE " "
+    NAME
+      IDENT "f"
+    PARAM_LIST
+      L_PAREN "("
+      R_PAREN ")"
+    WHITESPACE " "
+    BLOCK_EXPR
+      STMT_LIST
+        L_CURLY "{"
+        WHITESPACE "\n    "
+        LET_STMT
+          LET_KW "let"
+          WHITESPACE " "
+          WILDCARD_PAT
+            UNDERSCORE "_"
+          WHITESPACE " "
+          EQ "="
+          WHITESPACE " "
+          IF_EXPR
+            IF_KW "if"
+            WHITESPACE " "
+            LITERAL
+              TRUE_KW "true"
+            WHITESPACE " "
+            BLOCK_EXPR
+              STMT_LIST
+                L_CURLY "{"
+                WHITESPACE "\n    "
+                R_CURLY "}"
+            WHITESPACE " "
+            ELSE_KW "else"
+            WHITESPACE " "
+            BLOCK_EXPR
+              STMT_LIST
+                L_CURLY "{"
+                WHITESPACE "\n    "
+                R_CURLY "}"
+          WHITESPACE " "
+          LET_ELSE
+            ELSE_KW "else"
+            WHITESPACE " "
+            BLOCK_EXPR
+              STMT_LIST
+                L_CURLY "{"
+                WHITESPACE "\n        "
+                RETURN_EXPR
+                  RETURN_KW "return"
+                WHITESPACE "\n    "
+                R_CURLY "}"
+          SEMICOLON ";"
+        WHITESPACE "\n"
+        R_CURLY "}"
+error 49: right curly brace `}` before `else` in a `let...else` statement not allowed
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0053_let_else_right_curly_brace_if.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0053_let_else_right_curly_brace_if.rs
new file mode 100644
index 00000000000..9a87aecbd41
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0053_let_else_right_curly_brace_if.rs
@@ -0,0 +1,7 @@
+fn f() {
+    let _ = if true {
+    } else {
+    } else {
+        return
+    };
+}
\ No newline at end of file
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0016_angled_path_without_qual.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0016_angled_path_without_qual.rast
new file mode 100644
index 00000000000..0529e9750e7
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0016_angled_path_without_qual.rast
@@ -0,0 +1,49 @@
+SOURCE_FILE
+  TYPE_ALIAS
+    TYPE_KW "type"
+    WHITESPACE " "
+    NAME
+      IDENT "X"
+    WHITESPACE " "
+    EQ "="
+    WHITESPACE " "
+    PATH_TYPE
+      PATH
+        PATH_SEGMENT
+          L_ANGLE "<"
+          TUPLE_TYPE
+            L_PAREN "("
+            R_PAREN ")"
+          R_ANGLE ">"
+    SEMICOLON ";"
+  WHITESPACE "\n"
+  TYPE_ALIAS
+    TYPE_KW "type"
+    WHITESPACE " "
+    NAME
+      IDENT "Y"
+    WHITESPACE " "
+    EQ "="
+    WHITESPACE " "
+    PATH_TYPE
+      PATH
+        PATH_SEGMENT
+          L_ANGLE "<"
+          PATH_TYPE
+            PATH
+              PATH_SEGMENT
+                NAME_REF
+                  IDENT "A"
+          WHITESPACE " "
+          AS_KW "as"
+          WHITESPACE " "
+          PATH_TYPE
+            PATH
+              PATH_SEGMENT
+                NAME_REF
+                  IDENT "B"
+          R_ANGLE ">"
+    SEMICOLON ";"
+  WHITESPACE "\n"
+error 13: expected `::`
+error 32: expected `::`
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0016_angled_path_without_qual.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0016_angled_path_without_qual.rs
new file mode 100644
index 00000000000..802d6cc14a4
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0016_angled_path_without_qual.rs
@@ -0,0 +1,2 @@
+type X = <()>;
+type Y = <A as B>;
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0017_let_else_right_curly_brace.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0017_let_else_right_curly_brace.rast
new file mode 100644
index 00000000000..6ec580212b4
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0017_let_else_right_curly_brace.rast
@@ -0,0 +1,69 @@
+SOURCE_FILE
+  FN
+    FN_KW "fn"
+    WHITESPACE " "
+    NAME
+      IDENT "func"
+    PARAM_LIST
+      L_PAREN "("
+      R_PAREN ")"
+    WHITESPACE " "
+    BLOCK_EXPR
+      STMT_LIST
+        L_CURLY "{"
+        WHITESPACE " "
+        LET_STMT
+          LET_KW "let"
+          WHITESPACE " "
+          TUPLE_STRUCT_PAT
+            PATH
+              PATH_SEGMENT
+                NAME_REF
+                  IDENT "Some"
+            L_PAREN "("
+            WILDCARD_PAT
+              UNDERSCORE "_"
+            R_PAREN ")"
+          WHITESPACE " "
+          EQ "="
+          WHITESPACE " "
+          BLOCK_EXPR
+            STMT_LIST
+              L_CURLY "{"
+              CALL_EXPR
+                PATH_EXPR
+                  PATH
+                    PATH_SEGMENT
+                      NAME_REF
+                        IDENT "Some"
+                ARG_LIST
+                  L_PAREN "("
+                  LITERAL
+                    INT_NUMBER "1"
+                  R_PAREN ")"
+              R_CURLY "}"
+          WHITESPACE " "
+          LET_ELSE
+            ELSE_KW "else"
+            WHITESPACE " "
+            BLOCK_EXPR
+              STMT_LIST
+                L_CURLY "{"
+                WHITESPACE " "
+                MACRO_EXPR
+                  MACRO_CALL
+                    PATH
+                      PATH_SEGMENT
+                        NAME_REF
+                          IDENT "panic"
+                    BANG "!"
+                    TOKEN_TREE
+                      L_PAREN "("
+                      STRING "\"h\""
+                      R_PAREN ")"
+                WHITESPACE " "
+                R_CURLY "}"
+          SEMICOLON ";"
+        R_CURLY "}"
+  WHITESPACE "\n"
+error 35: right curly brace `}` before `else` in a `let...else` statement not allowed
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0017_let_else_right_curly_brace.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0017_let_else_right_curly_brace.rs
new file mode 100644
index 00000000000..30d52fea3b2
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0017_let_else_right_curly_brace.rs
@@ -0,0 +1 @@
+fn func() { let Some(_) = {Some(1)} else { panic!("h") };}
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0151_trait_alias.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0151_trait_alias.rast
index 2ef66484ae4..c45f8708980 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0151_trait_alias.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0151_trait_alias.rast
@@ -1,5 +1,5 @@
 SOURCE_FILE
-  TRAIT
+  TRAIT_ALIAS
     TRAIT_KW "trait"
     WHITESPACE " "
     NAME
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0177_trait_alias_where_clause.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0177_trait_alias_where_clause.rast
index 4443d9d1426..8f678247731 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0177_trait_alias_where_clause.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0177_trait_alias_where_clause.rast
@@ -1,5 +1,5 @@
 SOURCE_FILE
-  TRAIT
+  TRAIT_ALIAS
     TRAIT_KW "trait"
     WHITESPACE " "
     NAME
@@ -50,7 +50,7 @@ SOURCE_FILE
                     IDENT "Copy"
     SEMICOLON ";"
   WHITESPACE "\n"
-  TRAIT
+  TRAIT_ALIAS
     TRAIT_KW "trait"
     WHITESPACE " "
     NAME
diff --git a/src/tools/rust-analyzer/crates/project-model/src/build_scripts.rs b/src/tools/rust-analyzer/crates/project-model/src/build_scripts.rs
index 6550cf27e99..6df1273edd6 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/build_scripts.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/build_scripts.rs
@@ -15,13 +15,13 @@ use std::{
 
 use cargo_metadata::{camino::Utf8Path, Message};
 use la_arena::ArenaMap;
-use paths::AbsPathBuf;
+use paths::{AbsPath, AbsPathBuf};
 use rustc_hash::FxHashMap;
 use semver::Version;
 use serde::Deserialize;
 
 use crate::{
-    cfg_flag::CfgFlag, CargoConfig, CargoFeatures, CargoWorkspace, InvocationLocation,
+    cfg_flag::CfgFlag, utf8_stdout, CargoConfig, CargoFeatures, CargoWorkspace, InvocationLocation,
     InvocationStrategy, Package,
 };
 
@@ -67,6 +67,7 @@ impl WorkspaceBuildScripts {
                 let mut cmd = Command::new(toolchain::cargo());
 
                 cmd.args(["check", "--quiet", "--workspace", "--message-format=json"]);
+                cmd.args(&config.extra_args);
 
                 // --all-targets includes tests, benches and examples in addition to the
                 // default lib and bins. This is an independent concept from the --target
@@ -250,7 +251,7 @@ impl WorkspaceBuildScripts {
 
         if tracing::enabled!(tracing::Level::INFO) {
             for package in workspace.packages() {
-                let package_build_data = &mut outputs[package];
+                let package_build_data = &outputs[package];
                 if !package_build_data.is_unchanged() {
                     tracing::info!(
                         "{}: {:?}",
@@ -378,6 +379,83 @@ impl WorkspaceBuildScripts {
     pub(crate) fn get_output(&self, idx: Package) -> Option<&BuildScriptOutput> {
         self.outputs.get(idx)
     }
+
+    pub(crate) fn rustc_crates(
+        rustc: &CargoWorkspace,
+        current_dir: &AbsPath,
+        extra_env: &FxHashMap<String, String>,
+    ) -> Self {
+        let mut bs = WorkspaceBuildScripts::default();
+        for p in rustc.packages() {
+            bs.outputs.insert(p, BuildScriptOutput::default());
+        }
+        let res = (|| {
+            let target_libdir = (|| {
+                let mut cargo_config = Command::new(toolchain::cargo());
+                cargo_config.envs(extra_env);
+                cargo_config
+                    .current_dir(current_dir)
+                    .args(["rustc", "-Z", "unstable-options", "--print", "target-libdir"])
+                    .env("RUSTC_BOOTSTRAP", "1");
+                if let Ok(it) = utf8_stdout(cargo_config) {
+                    return Ok(it);
+                }
+                let mut cmd = Command::new(toolchain::rustc());
+                cmd.envs(extra_env);
+                cmd.args(["--print", "target-libdir"]);
+                utf8_stdout(cmd)
+            })()?;
+
+            let target_libdir = AbsPathBuf::try_from(PathBuf::from(target_libdir))
+                .map_err(|_| anyhow::format_err!("target-libdir was not an absolute path"))?;
+            tracing::info!("Loading rustc proc-macro paths from {}", target_libdir.display());
+
+            let proc_macro_dylibs: Vec<(String, AbsPathBuf)> = std::fs::read_dir(target_libdir)?
+                .filter_map(|entry| {
+                    let dir_entry = entry.ok()?;
+                    if dir_entry.file_type().ok()?.is_file() {
+                        let path = dir_entry.path();
+                        tracing::info!("p{:?}", path);
+                        let extension = path.extension()?;
+                        if extension == std::env::consts::DLL_EXTENSION {
+                            let name = path.file_stem()?.to_str()?.split_once('-')?.0.to_owned();
+                            let path = AbsPathBuf::try_from(path).ok()?;
+                            return Some((name, path));
+                        }
+                    }
+                    None
+                })
+                .collect();
+            for p in rustc.packages() {
+                let package = &rustc[p];
+                if package.targets.iter().any(|&it| rustc[it].is_proc_macro) {
+                    if let Some((_, path)) =
+                        proc_macro_dylibs.iter().find(|(name, _)| *name == package.name)
+                    {
+                        bs.outputs[p].proc_macro_dylib_path = Some(path.clone());
+                    }
+                }
+            }
+
+            if tracing::enabled!(tracing::Level::INFO) {
+                for package in rustc.packages() {
+                    let package_build_data = &bs.outputs[package];
+                    if !package_build_data.is_unchanged() {
+                        tracing::info!(
+                            "{}: {:?}",
+                            rustc[package].manifest.parent().display(),
+                            package_build_data,
+                        );
+                    }
+                }
+            }
+            Ok(())
+        })();
+        if let Err::<_, anyhow::Error>(e) = res {
+            bs.error = Some(e.to_string());
+        }
+        bs
+    }
 }
 
 // FIXME: Find a better way to know if it is a dylib.
diff --git a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs
index fdc7859eb90..732adc50b50 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs
@@ -105,6 +105,8 @@ pub struct CargoConfig {
     pub wrap_rustc_in_build_scripts: bool,
     /// The command to run instead of `cargo check` for building build scripts.
     pub run_build_script_command: Option<Vec<String>>,
+    /// Extra args to pass to the cargo command.
+    pub extra_args: Vec<String>,
     /// Extra env vars to set when invoking the cargo command
     pub extra_env: FxHashMap<String, String>,
     pub invocation_strategy: InvocationStrategy,
diff --git a/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs b/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs
index 328d2fbcf31..74e41eda763 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs
@@ -88,23 +88,17 @@ impl Sysroot {
     }
 
     pub fn discover_with_src_override(
-        dir: &AbsPath,
+        current_dir: &AbsPath,
         extra_env: &FxHashMap<String, String>,
         src: AbsPathBuf,
     ) -> Result<Sysroot> {
-        tracing::debug!("discovering sysroot for {}", dir.display());
-        let sysroot_dir = discover_sysroot_dir(dir, extra_env)?;
+        tracing::debug!("discovering sysroot for {}", current_dir.display());
+        let sysroot_dir = discover_sysroot_dir(current_dir, extra_env)?;
         Ok(Sysroot::load(sysroot_dir, src))
     }
 
-    pub fn discover_rustc(
-        cargo_toml: &ManifestPath,
-        extra_env: &FxHashMap<String, String>,
-    ) -> Option<ManifestPath> {
-        tracing::debug!("discovering rustc source for {}", cargo_toml.display());
-        let current_dir = cargo_toml.parent();
-        let sysroot_dir = discover_sysroot_dir(current_dir, extra_env).ok()?;
-        get_rustc_src(&sysroot_dir)
+    pub fn discover_rustc(&self) -> Option<ManifestPath> {
+        get_rustc_src(&self.root)
     }
 
     pub fn with_sysroot_dir(sysroot_dir: AbsPathBuf) -> Result<Sysroot> {
@@ -282,4 +276,7 @@ unwind
 std_detect
 test";
 
-const PROC_MACRO_DEPS: &str = "std";
+// core is required for our builtin derives to work in the proc_macro lib currently
+const PROC_MACRO_DEPS: &str = "
+std
+core";
diff --git a/src/tools/rust-analyzer/crates/project-model/src/tests.rs b/src/tools/rust-analyzer/crates/project-model/src/tests.rs
index 9e9691d11e8..749eee531ee 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/tests.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/tests.rs
@@ -1547,6 +1547,15 @@ fn rust_project_hello_world_project_model() {
                                 ),
                                 prelude: true,
                             },
+                            Dependency {
+                                crate_id: CrateId(
+                                    1,
+                                ),
+                                name: CrateName(
+                                    "core",
+                                ),
+                                prelude: true,
+                            },
                         ],
                         proc_macro: Err(
                             "no proc macro loaded for sysroot crate",
diff --git a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs
index 2a11f1e8eb8..faa6816fdc2 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs
@@ -70,7 +70,7 @@ pub enum ProjectWorkspace {
         cargo: CargoWorkspace,
         build_scripts: WorkspaceBuildScripts,
         sysroot: Option<Sysroot>,
-        rustc: Option<CargoWorkspace>,
+        rustc: Option<(CargoWorkspace, WorkspaceBuildScripts)>,
         /// Holds cfg flags for the current target. We get those by running
         /// `rustc --print cfg`.
         ///
@@ -116,7 +116,7 @@ impl fmt::Debug for ProjectWorkspace {
                 .field("sysroot", &sysroot.is_some())
                 .field(
                     "n_rustc_compiler_crates",
-                    &rustc.as_ref().map_or(0, |rc| rc.packages().len()),
+                    &rustc.as_ref().map_or(0, |(rc, _)| rc.packages().len()),
                 )
                 .field("n_rustc_cfg", &rustc_cfg.len())
                 .field("n_cfg_overrides", &cfg_overrides.len())
@@ -237,37 +237,45 @@ impl ProjectWorkspace {
                 };
 
                 if let Some(sysroot) = &sysroot {
-                    tracing::info!(src_root = %sysroot.src_root().display(), root = %sysroot.root().display(), "Using sysroot");
+                    tracing::info!(workspace = %cargo_toml.display(), src_root = %sysroot.src_root().display(), root = %sysroot.root().display(), "Using sysroot");
                 }
 
                 let rustc_dir = match &config.rustc_source {
                     Some(RustcSource::Path(path)) => ManifestPath::try_from(path.clone()).ok(),
                     Some(RustcSource::Discover) => {
-                        Sysroot::discover_rustc(&cargo_toml, &config.extra_env)
+                        sysroot.as_ref().and_then(Sysroot::discover_rustc)
                     }
                     None => None,
                 };
-                if let Some(rustc_dir) = &rustc_dir {
-                    tracing::info!(rustc_dir = %rustc_dir.display(), "Using rustc source");
-                }
 
                 let rustc = match rustc_dir {
-                    Some(rustc_dir) => match CargoWorkspace::fetch_metadata(
-                        &rustc_dir,
-                        cargo_toml.parent(),
-                        config,
-                        progress,
-                    ) {
-                        Ok(meta) => Some(CargoWorkspace::new(meta)),
-                        Err(e) => {
-                            tracing::error!(
-                                %e,
-                                "Failed to read Cargo metadata from rustc source at {}",
-                                rustc_dir.display()
-                            );
-                            None
+                    Some(rustc_dir) => {
+                        tracing::info!(workspace = %cargo_toml.display(), rustc_dir = %rustc_dir.display(), "Using rustc source");
+                        match CargoWorkspace::fetch_metadata(
+                            &rustc_dir,
+                            cargo_toml.parent(),
+                            config,
+                            progress,
+                        ) {
+                            Ok(meta) => {
+                                let workspace = CargoWorkspace::new(meta);
+                                let buildscripts = WorkspaceBuildScripts::rustc_crates(
+                                    &workspace,
+                                    cargo_toml.parent(),
+                                    &config.extra_env,
+                                );
+                                Some((workspace, buildscripts))
+                            }
+                            Err(e) => {
+                                tracing::error!(
+                                    %e,
+                                    "Failed to read Cargo metadata from rustc source at {}",
+                                    rustc_dir.display()
+                                );
+                                None
+                            }
                         }
-                    },
+                    }
                     None => None,
                 };
 
@@ -531,7 +539,7 @@ impl ProjectWorkspace {
                         PackageRoot { is_local, include, exclude }
                     })
                     .chain(mk_sysroot(sysroot.as_ref(), Some(cargo.workspace_root())))
-                    .chain(rustc.iter().flat_map(|rustc| {
+                    .chain(rustc.iter().flat_map(|(rustc, _)| {
                         rustc.packages().map(move |krate| PackageRoot {
                             is_local: false,
                             include: vec![rustc[krate].manifest.parent().to_path_buf()],
@@ -559,7 +567,7 @@ impl ProjectWorkspace {
                 sysroot_package_len + project.n_crates()
             }
             ProjectWorkspace::Cargo { cargo, sysroot, rustc, .. } => {
-                let rustc_package_len = rustc.as_ref().map_or(0, |it| it.packages().len());
+                let rustc_package_len = rustc.as_ref().map_or(0, |(it, _)| it.packages().len());
                 let sysroot_package_len = sysroot.as_ref().map_or(0, |it| it.crates().len());
                 cargo.packages().len() + sysroot_package_len + rustc_package_len
             }
@@ -778,7 +786,7 @@ fn project_json_to_crate_graph(
 fn cargo_to_crate_graph(
     load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> ProcMacroLoadResult,
     load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
-    rustc: &Option<CargoWorkspace>,
+    rustc: &Option<(CargoWorkspace, WorkspaceBuildScripts)>,
     cargo: &CargoWorkspace,
     sysroot: Option<&Sysroot>,
     rustc_cfg: Vec<CfgFlag>,
@@ -924,7 +932,7 @@ fn cargo_to_crate_graph(
     if has_private {
         // If the user provided a path to rustc sources, we add all the rustc_private crates
         // and create dependencies on them for the crates which opt-in to that
-        if let Some(rustc_workspace) = rustc {
+        if let Some((rustc_workspace, build_scripts)) = rustc {
             handle_rustc_crates(
                 &mut crate_graph,
                 &mut pkg_to_lib_crate,
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/default_12483297303756020505_0.profraw b/src/tools/rust-analyzer/crates/rust-analyzer/default_12483297303756020505_0.profraw
deleted file mode 100644
index e49d7c14492..00000000000
--- a/src/tools/rust-analyzer/crates/rust-analyzer/default_12483297303756020505_0.profraw
+++ /dev/null
Binary files differdiff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs
index 93297faa664..e8c10927d62 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -12,7 +12,7 @@ use hir::{
 };
 use hir_def::{
     body::{BodySourceMap, SyntheticSyntax},
-    expr::ExprId,
+    expr::{ExprId, PatId},
     FunctionId,
 };
 use hir_ty::{Interner, TyExt, TypeFlags};
@@ -222,7 +222,11 @@ impl flags::AnalysisStats {
         let mut num_exprs = 0;
         let mut num_exprs_unknown = 0;
         let mut num_exprs_partially_unknown = 0;
-        let mut num_type_mismatches = 0;
+        let mut num_expr_type_mismatches = 0;
+        let mut num_pats = 0;
+        let mut num_pats_unknown = 0;
+        let mut num_pats_partially_unknown = 0;
+        let mut num_pat_type_mismatches = 0;
         let analysis = host.analysis();
         for f in funcs.iter().copied() {
             let name = f.name(db);
@@ -255,6 +259,8 @@ impl flags::AnalysisStats {
             let f_id = FunctionId::from(f);
             let (body, sm) = db.body_with_source_map(f_id.into());
             let inference_result = db.infer(f_id.into());
+
+            // region:expressions
             let (previous_exprs, previous_unknown, previous_partially_unknown) =
                 (num_exprs, num_exprs_unknown, num_exprs_partially_unknown);
             for (expr_id, _) in body.exprs.iter() {
@@ -307,12 +313,12 @@ impl flags::AnalysisStats {
                 if unknown_or_partial && self.output == Some(OutputFormat::Csv) {
                     println!(
                         r#"{},type,"{}""#,
-                        location_csv(db, &analysis, vfs, &sm, expr_id),
+                        location_csv_expr(db, &analysis, vfs, &sm, expr_id),
                         ty.display(db)
                     );
                 }
                 if let Some(mismatch) = inference_result.type_mismatch_for_expr(expr_id) {
-                    num_type_mismatches += 1;
+                    num_expr_type_mismatches += 1;
                     if verbosity.is_verbose() {
                         if let Some((path, start, end)) =
                             expr_syntax_range(db, &analysis, vfs, &sm, expr_id)
@@ -339,7 +345,7 @@ impl flags::AnalysisStats {
                     if self.output == Some(OutputFormat::Csv) {
                         println!(
                             r#"{},mismatch,"{}","{}""#,
-                            location_csv(db, &analysis, vfs, &sm, expr_id),
+                            location_csv_expr(db, &analysis, vfs, &sm, expr_id),
                             mismatch.expected.display(db),
                             mismatch.actual.display(db)
                         );
@@ -355,6 +361,109 @@ impl flags::AnalysisStats {
                     num_exprs_partially_unknown - previous_partially_unknown
                 ));
             }
+            // endregion:expressions
+
+            // region:patterns
+            let (previous_pats, previous_unknown, previous_partially_unknown) =
+                (num_pats, num_pats_unknown, num_pats_partially_unknown);
+            for (pat_id, _) in body.pats.iter() {
+                let ty = &inference_result[pat_id];
+                num_pats += 1;
+                let unknown_or_partial = if ty.is_unknown() {
+                    num_pats_unknown += 1;
+                    if verbosity.is_spammy() {
+                        if let Some((path, start, end)) =
+                            pat_syntax_range(db, &analysis, vfs, &sm, pat_id)
+                        {
+                            bar.println(format!(
+                                "{} {}:{}-{}:{}: Unknown type",
+                                path,
+                                start.line + 1,
+                                start.col,
+                                end.line + 1,
+                                end.col,
+                            ));
+                        } else {
+                            bar.println(format!("{name}: Unknown type",));
+                        }
+                    }
+                    true
+                } else {
+                    let is_partially_unknown =
+                        ty.data(Interner).flags.contains(TypeFlags::HAS_ERROR);
+                    if is_partially_unknown {
+                        num_pats_partially_unknown += 1;
+                    }
+                    is_partially_unknown
+                };
+                if self.only.is_some() && verbosity.is_spammy() {
+                    // in super-verbose mode for just one function, we print every single pattern
+                    if let Some((_, start, end)) = pat_syntax_range(db, &analysis, vfs, &sm, pat_id)
+                    {
+                        bar.println(format!(
+                            "{}:{}-{}:{}: {}",
+                            start.line + 1,
+                            start.col,
+                            end.line + 1,
+                            end.col,
+                            ty.display(db)
+                        ));
+                    } else {
+                        bar.println(format!("unknown location: {}", ty.display(db)));
+                    }
+                }
+                if unknown_or_partial && self.output == Some(OutputFormat::Csv) {
+                    println!(
+                        r#"{},type,"{}""#,
+                        location_csv_pat(db, &analysis, vfs, &sm, pat_id),
+                        ty.display(db)
+                    );
+                }
+                if let Some(mismatch) = inference_result.type_mismatch_for_pat(pat_id) {
+                    num_pat_type_mismatches += 1;
+                    if verbosity.is_verbose() {
+                        if let Some((path, start, end)) =
+                            pat_syntax_range(db, &analysis, vfs, &sm, pat_id)
+                        {
+                            bar.println(format!(
+                                "{} {}:{}-{}:{}: Expected {}, got {}",
+                                path,
+                                start.line + 1,
+                                start.col,
+                                end.line + 1,
+                                end.col,
+                                mismatch.expected.display(db),
+                                mismatch.actual.display(db)
+                            ));
+                        } else {
+                            bar.println(format!(
+                                "{}: Expected {}, got {}",
+                                name,
+                                mismatch.expected.display(db),
+                                mismatch.actual.display(db)
+                            ));
+                        }
+                    }
+                    if self.output == Some(OutputFormat::Csv) {
+                        println!(
+                            r#"{},mismatch,"{}","{}""#,
+                            location_csv_pat(db, &analysis, vfs, &sm, pat_id),
+                            mismatch.expected.display(db),
+                            mismatch.actual.display(db)
+                        );
+                    }
+                }
+            }
+            if verbosity.is_spammy() {
+                bar.println(format!(
+                    "In {}: {} pats, {} unknown, {} partial",
+                    full_name,
+                    num_pats - previous_pats,
+                    num_pats_unknown - previous_unknown,
+                    num_pats_partially_unknown - previous_partially_unknown
+                ));
+            }
+            // endregion:patterns
             bar.inc(1);
         }
 
@@ -366,10 +475,21 @@ impl flags::AnalysisStats {
             percentage(num_exprs_unknown, num_exprs),
             num_exprs_partially_unknown,
             percentage(num_exprs_partially_unknown, num_exprs),
-            num_type_mismatches
+            num_expr_type_mismatches
+        );
+        eprintln!(
+            "  pats: {}, ??ty: {} ({}%), ?ty: {} ({}%), !ty: {}",
+            num_pats,
+            num_pats_unknown,
+            percentage(num_pats_unknown, num_pats),
+            num_pats_partially_unknown,
+            percentage(num_pats_partially_unknown, num_pats),
+            num_pat_type_mismatches
         );
         report_metric("unknown type", num_exprs_unknown, "#");
-        report_metric("type mismatches", num_type_mismatches, "#");
+        report_metric("type mismatches", num_expr_type_mismatches, "#");
+        report_metric("pattern unknown type", num_pats_unknown, "#");
+        report_metric("pattern type mismatches", num_pat_type_mismatches, "#");
 
         eprintln!("{:<20} {}", "Inference:", inference_sw.elapsed());
     }
@@ -379,7 +499,7 @@ impl flags::AnalysisStats {
     }
 }
 
-fn location_csv(
+fn location_csv_expr(
     db: &RootDatabase,
     analysis: &Analysis,
     vfs: &Vfs,
@@ -401,6 +521,30 @@ fn location_csv(
     format!("{path},{}:{},{}:{}", start.line + 1, start.col, end.line + 1, end.col)
 }
 
+fn location_csv_pat(
+    db: &RootDatabase,
+    analysis: &Analysis,
+    vfs: &Vfs,
+    sm: &BodySourceMap,
+    pat_id: PatId,
+) -> String {
+    let src = match sm.pat_syntax(pat_id) {
+        Ok(s) => s,
+        Err(SyntheticSyntax) => return "synthetic,,".to_string(),
+    };
+    let root = db.parse_or_expand(src.file_id).unwrap();
+    let node = src.map(|e| {
+        e.either(|it| it.to_node(&root).syntax().clone(), |it| it.to_node(&root).syntax().clone())
+    });
+    let original_range = node.as_ref().original_file_range(db);
+    let path = vfs.file_path(original_range.file_id);
+    let line_index = analysis.file_line_index(original_range.file_id).unwrap();
+    let text_range = original_range.range;
+    let (start, end) =
+        (line_index.line_col(text_range.start()), line_index.line_col(text_range.end()));
+    format!("{path},{}:{},{}:{}", start.line + 1, start.col, end.line + 1, end.col)
+}
+
 fn expr_syntax_range(
     db: &RootDatabase,
     analysis: &Analysis,
@@ -423,6 +567,33 @@ fn expr_syntax_range(
         None
     }
 }
+fn pat_syntax_range(
+    db: &RootDatabase,
+    analysis: &Analysis,
+    vfs: &Vfs,
+    sm: &BodySourceMap,
+    pat_id: PatId,
+) -> Option<(VfsPath, LineCol, LineCol)> {
+    let src = sm.pat_syntax(pat_id);
+    if let Ok(src) = src {
+        let root = db.parse_or_expand(src.file_id).unwrap();
+        let node = src.map(|e| {
+            e.either(
+                |it| it.to_node(&root).syntax().clone(),
+                |it| it.to_node(&root).syntax().clone(),
+            )
+        });
+        let original_range = node.as_ref().original_file_range(db);
+        let path = vfs.file_path(original_range.file_id);
+        let line_index = analysis.file_line_index(original_range.file_id).unwrap();
+        let text_range = original_range.range;
+        let (start, end) =
+            (line_index.line_col(text_range.start()), line_index.line_col(text_range.end()));
+        Some((path, start, end))
+    } else {
+        None
+    }
+}
 
 fn shuffle<T>(rng: &mut Rand32, slice: &mut [T]) {
     for i in 0..slice.len() {
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs
index ff821be53d8..0721d486ef1 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs
@@ -1,6 +1,7 @@
 //! Analyze all modules in a project for diagnostics. Exits with a non-zero
 //! status code if any errors are found.
 
+use project_model::{CargoConfig, RustcSource};
 use rustc_hash::FxHashSet;
 
 use hir::{db::HirDatabase, Crate, Module};
@@ -14,7 +15,8 @@ use crate::cli::{
 
 impl flags::Diagnostics {
     pub fn run(self) -> anyhow::Result<()> {
-        let cargo_config = Default::default();
+        let mut cargo_config = CargoConfig::default();
+        cargo_config.sysroot = Some(RustcSource::Discover);
         let load_cargo_config = LoadCargoConfig {
             load_out_dirs_from_check: !self.disable_build_scripts,
             with_proc_macro_server: ProcMacroServerChoice::Sysroot,
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs
index 3fc1aa4eaeb..9b5451496c6 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs
@@ -13,7 +13,7 @@ use ide_db::LineIndexDatabase;
 use ide_db::base_db::salsa::{self, ParallelDatabase};
 use ide_db::line_index::WideEncoding;
 use lsp_types::{self, lsif};
-use project_model::{CargoConfig, ProjectManifest, ProjectWorkspace};
+use project_model::{CargoConfig, ProjectManifest, ProjectWorkspace, RustcSource};
 use vfs::{AbsPathBuf, Vfs};
 
 use crate::cli::load_cargo::ProcMacroServerChoice;
@@ -289,7 +289,8 @@ impl flags::Lsif {
     pub fn run(self) -> Result<()> {
         eprintln!("Generating LSIF started...");
         let now = Instant::now();
-        let cargo_config = CargoConfig::default();
+        let mut cargo_config = CargoConfig::default();
+        cargo_config.sysroot = Some(RustcSource::Discover);
         let no_progress = &|_| ();
         let load_cargo_config = LoadCargoConfig {
             load_out_dirs_from_check: true,
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs
index 9a04fbea774..df5c26cf77a 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs
@@ -15,7 +15,7 @@ use ide::{
     TokenStaticData,
 };
 use ide_db::LineIndexDatabase;
-use project_model::{CargoConfig, ProjectManifest, ProjectWorkspace};
+use project_model::{CargoConfig, ProjectManifest, ProjectWorkspace, RustcSource};
 use scip::types as scip_types;
 use std::env;
 
@@ -29,7 +29,8 @@ impl flags::Scip {
     pub fn run(self) -> Result<()> {
         eprintln!("Generating SCIP start...");
         let now = Instant::now();
-        let cargo_config = CargoConfig::default();
+        let mut cargo_config = CargoConfig::default();
+        cargo_config.sysroot = Some(RustcSource::Discover);
 
         let no_progress = &|s| (eprintln!("rust-analyzer: Loading {s}"));
         let load_cargo_config = LoadCargoConfig {
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/ssr.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/ssr.rs
index 3552f840a1b..35a874f8920 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/ssr.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/ssr.rs
@@ -1,7 +1,7 @@
 //! Applies structured search replace rules from the command line.
 
 use ide_ssr::MatchFinder;
-use project_model::CargoConfig;
+use project_model::{CargoConfig, RustcSource};
 
 use crate::cli::{
     flags,
@@ -12,7 +12,8 @@ use crate::cli::{
 impl flags::Ssr {
     pub fn run(self) -> Result<()> {
         use ide_db::base_db::SourceDatabaseExt;
-        let cargo_config = CargoConfig::default();
+        let mut cargo_config = CargoConfig::default();
+        cargo_config.sysroot = Some(RustcSource::Discover);
         let load_cargo_config = LoadCargoConfig {
             load_out_dirs_from_check: true,
             with_proc_macro_server: ProcMacroServerChoice::Sysroot,
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
index f609a50a05f..75233dbb2ab 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
@@ -101,6 +101,8 @@ config_data! {
         /// Use `RUSTC_WRAPPER=rust-analyzer` when running build scripts to
         /// avoid checking unnecessary things.
         cargo_buildScripts_useRustcWrapper: bool = "true",
+        /// Extra arguments that are passed to every cargo invocation.
+        cargo_extraArgs: Vec<String> = "[]",
         /// Extra environment variables that will be set when running cargo, rustc
         /// or other commands within the workspace. Useful for setting RUSTFLAGS.
         cargo_extraEnv: FxHashMap<String, String> = "{}",
@@ -366,6 +368,8 @@ config_data! {
         inlayHints_typeHints_hideClosureInitialization: bool       = "false",
         /// Whether to hide inlay type hints for constructors.
         inlayHints_typeHints_hideNamedConstructor: bool            = "false",
+        /// Enables the experimental support for interpreting tests.
+        interpret_tests: bool                                      = "false",
 
         /// Join lines merges consecutive declaration and initialization of an assignment.
         joinLines_joinAssignments: bool = "true",
@@ -456,7 +460,10 @@ config_data! {
         /// Additional arguments to `rustfmt`.
         rustfmt_extraArgs: Vec<String>               = "[]",
         /// Advanced option, fully override the command rust-analyzer uses for
-        /// formatting.
+        /// formatting. This should be the equivalent of `rustfmt` here, and
+        /// not that of `cargo fmt`. The file contents will be passed on the
+        /// standard input and the formatted result will be read from the
+        /// standard output.
         rustfmt_overrideCommand: Option<Vec<String>> = "null",
         /// Enables the use of rustfmt's unstable range formatting command for the
         /// `textDocument/rangeFormatting` request. The rustfmt option is unstable and only
@@ -849,27 +856,27 @@ impl Config {
     }
     pub fn linked_projects(&self) -> Vec<LinkedProject> {
         match self.data.linkedProjects.as_slice() {
-            [] => match self.discovered_projects.as_ref() {
-                Some(discovered_projects) => {
-                    let exclude_dirs: Vec<_> = self
-                        .data
-                        .files_excludeDirs
+            [] => {
+                match self.discovered_projects.as_ref() {
+                    Some(discovered_projects) => {
+                        let exclude_dirs: Vec<_> = self
+                            .data
+                            .files_excludeDirs
+                            .iter()
+                            .map(|p| self.root_path.join(p))
+                            .collect();
+                        discovered_projects
                         .iter()
-                        .map(|p| self.root_path.join(p))
-                        .collect();
-                    discovered_projects
-                        .iter()
-                        .filter(|p| {
-                            let (ProjectManifest::ProjectJson(path)
-                            | ProjectManifest::CargoToml(path)) = p;
+                        .filter(|(ProjectManifest::ProjectJson(path) | ProjectManifest::CargoToml(path))| {
                             !exclude_dirs.iter().any(|p| path.starts_with(p))
                         })
                         .cloned()
                         .map(LinkedProject::from)
                         .collect()
+                    }
+                    None => Vec::new(),
                 }
-                None => Vec::new(),
-            },
+            }
             linked_projects => linked_projects
                 .iter()
                 .filter_map(|linked_project| match linked_project {
@@ -1050,10 +1057,20 @@ impl Config {
         }
     }
 
+    pub fn extra_args(&self) -> &Vec<String> {
+        &self.data.cargo_extraArgs
+    }
+
     pub fn extra_env(&self) -> &FxHashMap<String, String> {
         &self.data.cargo_extraEnv
     }
 
+    pub fn check_extra_args(&self) -> Vec<String> {
+        let mut extra_args = self.extra_args().clone();
+        extra_args.extend_from_slice(&self.data.check_extraArgs);
+        extra_args
+    }
+
     pub fn check_extra_env(&self) -> FxHashMap<String, String> {
         let mut extra_env = self.data.cargo_extraEnv.clone();
         extra_env.extend(self.data.check_extraEnv.clone());
@@ -1152,6 +1169,7 @@ impl Config {
                 InvocationLocation::Workspace => project_model::InvocationLocation::Workspace,
             },
             run_build_script_command: self.data.cargo_buildScripts_overrideCommand.clone(),
+            extra_args: self.data.cargo_extraArgs.clone(),
             extra_env: self.data.cargo_extraEnv.clone(),
         }
     }
@@ -1222,7 +1240,7 @@ impl Config {
                     CargoFeaturesDef::All => vec![],
                     CargoFeaturesDef::Selected(it) => it,
                 },
-                extra_args: self.data.check_extraArgs.clone(),
+                extra_args: self.check_extra_args(),
                 extra_env: self.check_extra_env(),
                 ansi_color_output: self.color_diagnostic_output(),
             },
@@ -1441,6 +1459,7 @@ impl Config {
                 }
             },
             keywords: self.data.hover_documentation_keywords_enable,
+            interpret_tests: self.data.interpret_tests,
         }
     }
 
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers.rs
index 4e08bd0a724..32ac9a42dec 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers.rs
@@ -134,6 +134,16 @@ pub(crate) fn handle_view_hir(
     Ok(res)
 }
 
+pub(crate) fn handle_view_mir(
+    snap: GlobalStateSnapshot,
+    params: lsp_types::TextDocumentPositionParams,
+) -> Result<String> {
+    let _p = profile::span("handle_view_mir");
+    let position = from_proto::file_position(&snap, params)?;
+    let res = snap.analysis.view_mir(position)?;
+    Ok(res)
+}
+
 pub(crate) fn handle_view_file_text(
     snap: GlobalStateSnapshot,
     params: lsp_types::TextDocumentIdentifier,
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_ext.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_ext.rs
index e33589cc536..c7b513db981 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_ext.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_ext.rs
@@ -74,6 +74,14 @@ impl Request for ViewHir {
     const METHOD: &'static str = "rust-analyzer/viewHir";
 }
 
+pub enum ViewMir {}
+
+impl Request for ViewMir {
+    type Params = lsp_types::TextDocumentPositionParams;
+    type Result = String;
+    const METHOD: &'static str = "rust-analyzer/viewMir";
+}
+
 pub enum ViewFileText {}
 
 impl Request for ViewFileText {
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs
index d1e38b33c7d..dd0804b4398 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs
@@ -111,12 +111,7 @@ impl fmt::Debug for Event {
 
 impl GlobalState {
     fn run(mut self, inbox: Receiver<lsp_server::Message>) -> Result<()> {
-        if self.config.linked_projects().is_empty()
-            && self.config.detached_files().is_empty()
-            && self.config.notifications().cargo_toml_not_found
-        {
-            self.show_and_log_error("rust-analyzer failed to discover workspace".to_string(), None);
-        };
+        self.update_status_or_notify();
 
         if self.config.did_save_text_document_dynamic_registration() {
             let save_registration_options = lsp_types::TextDocumentSaveRegistrationOptions {
@@ -323,17 +318,6 @@ impl GlobalState {
 
         if let Some(diagnostic_changes) = self.diagnostics.take_changes() {
             for file_id in diagnostic_changes {
-                let db = self.analysis_host.raw_database();
-                let source_root = db.file_source_root(file_id);
-                if db.source_root(source_root).is_library {
-                    // Only publish diagnostics for files in the workspace, not from crates.io deps
-                    // or the sysroot.
-                    // While theoretically these should never have errors, we have quite a few false
-                    // positives particularly in the stdlib, and those diagnostics would stay around
-                    // forever if we emitted them here.
-                    continue;
-                }
-
                 let uri = file_id_to_url(&self.vfs.read().0, file_id);
                 let mut diagnostics =
                     self.diagnostics.diagnostics_for(file_id).cloned().collect::<Vec<_>>();
@@ -405,25 +389,28 @@ impl GlobalState {
             });
         }
 
+        self.update_status_or_notify();
+
+        let loop_duration = loop_start.elapsed();
+        if loop_duration > Duration::from_millis(100) && was_quiescent {
+            tracing::warn!("overly long loop turn: {:?}", loop_duration);
+            self.poke_rust_analyzer_developer(format!("overly long loop turn: {loop_duration:?}"));
+        }
+        Ok(())
+    }
+
+    fn update_status_or_notify(&mut self) {
         let status = self.current_status();
         if self.last_reported_status.as_ref() != Some(&status) {
             self.last_reported_status = Some(status.clone());
 
-            if let (lsp_ext::Health::Error, Some(message)) = (status.health, &status.message) {
-                self.show_message(lsp_types::MessageType::ERROR, message.clone());
-            }
-
             if self.config.server_status_notification() {
                 self.send_notification::<lsp_ext::ServerStatusNotification>(status);
+            } else if let (lsp_ext::Health::Error, Some(message)) = (status.health, &status.message)
+            {
+                self.show_and_log_error(message.clone(), None);
             }
         }
-
-        let loop_duration = loop_start.elapsed();
-        if loop_duration > Duration::from_millis(100) && was_quiescent {
-            tracing::warn!("overly long loop turn: {:?}", loop_duration);
-            self.poke_rust_analyzer_developer(format!("overly long loop turn: {loop_duration:?}"));
-        }
-        Ok(())
     }
 
     fn handle_task(&mut self, prime_caches_progress: &mut Vec<PrimeCachesProgress>, task: Task) {
@@ -456,6 +443,9 @@ impl GlobalState {
                     ProjectWorkspaceProgress::Report(msg) => (Progress::Report, Some(msg)),
                     ProjectWorkspaceProgress::End(workspaces) => {
                         self.fetch_workspaces_queue.op_completed(Some(workspaces));
+                        if let Err(e) = self.fetch_workspace_error() {
+                            tracing::error!("FetchWorkspaceError:\n{e}");
+                        }
 
                         let old = Arc::clone(&self.workspaces);
                         self.switch_workspaces("fetched workspace".to_string());
@@ -477,6 +467,9 @@ impl GlobalState {
                     BuildDataProgress::Report(msg) => (Some(Progress::Report), Some(msg)),
                     BuildDataProgress::End(build_data_result) => {
                         self.fetch_build_data_queue.op_completed(build_data_result);
+                        if let Err(e) = self.fetch_build_data_error() {
+                            tracing::error!("FetchBuildDataError:\n{e}");
+                        }
 
                         self.switch_workspaces("fetched build data".to_string());
 
@@ -509,6 +502,7 @@ impl GlobalState {
                 self.vfs_progress_n_total = n_total;
                 self.vfs_progress_n_done = n_done;
 
+                // if n_total != 0 {
                 let state = if n_done == 0 {
                     Progress::Begin
                 } else if n_done < n_total {
@@ -523,7 +517,8 @@ impl GlobalState {
                     Some(format!("{n_done}/{n_total}")),
                     Some(Progress::fraction(n_done, n_total)),
                     None,
-                )
+                );
+                // }
             }
         }
     }
@@ -565,7 +560,10 @@ impl GlobalState {
                     flycheck::Progress::DidCheckCrate(target) => (Progress::Report, Some(target)),
                     flycheck::Progress::DidCancel => (Progress::End, None),
                     flycheck::Progress::DidFailToRestart(err) => {
-                        self.show_and_log_error("cargo check failed".to_string(), Some(err));
+                        self.show_and_log_error(
+                            "cargo check failed to start".to_string(),
+                            Some(err),
+                        );
                         return;
                     }
                     flycheck::Progress::DidFinish(result) => {
@@ -634,6 +632,7 @@ impl GlobalState {
             .on::<lsp_ext::AnalyzerStatus>(handlers::handle_analyzer_status)
             .on::<lsp_ext::SyntaxTree>(handlers::handle_syntax_tree)
             .on::<lsp_ext::ViewHir>(handlers::handle_view_hir)
+            .on::<lsp_ext::ViewMir>(handlers::handle_view_mir)
             .on::<lsp_ext::ViewFileText>(handlers::handle_view_file_text)
             .on::<lsp_ext::ViewCrateGraph>(handlers::handle_view_crate_graph)
             .on::<lsp_ext::ViewItemTree>(handlers::handle_view_item_tree)
@@ -971,10 +970,20 @@ impl GlobalState {
     }
 
     fn update_diagnostics(&mut self) {
+        let db = self.analysis_host.raw_database();
         let subscriptions = self
             .mem_docs
             .iter()
             .map(|path| self.vfs.read().0.file_id(path).unwrap())
+            .filter(|&file_id| {
+                let source_root = db.file_source_root(file_id);
+                // Only publish diagnostics for files in the workspace, not from crates.io deps
+                // or the sysroot.
+                // While theoretically these should never have errors, we have quite a few false
+                // positives particularly in the stdlib, and those diagnostics would stay around
+                // forever if we emitted them here.
+                !db.source_root(source_root).is_library
+            })
             .collect::<Vec<_>>();
 
         tracing::trace!("updating notifications for {:?}", subscriptions);
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
index abce0d73782..28d37f5685a 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
@@ -12,17 +12,21 @@
 //! correct. Instead, we try to provide a best-effort service. Even if the
 //! project is currently loading and we don't have a full project model, we
 //! still want to respond to various  requests.
-use std::{mem, sync::Arc};
+use std::{collections::hash_map::Entry, mem, sync::Arc};
 
 use flycheck::{FlycheckConfig, FlycheckHandle};
 use hir::db::DefDatabase;
 use ide::Change;
-use ide_db::base_db::{
-    CrateGraph, Env, ProcMacro, ProcMacroExpander, ProcMacroExpansionError, ProcMacroKind,
-    ProcMacroLoadResult, SourceRoot, VfsPath,
+use ide_db::{
+    base_db::{
+        CrateGraph, Env, ProcMacro, ProcMacroExpander, ProcMacroExpansionError, ProcMacroKind,
+        ProcMacroLoadResult, SourceRoot, VfsPath,
+    },
+    FxHashMap,
 };
+use itertools::Itertools;
 use proc_macro_api::{MacroDylib, ProcMacroServer};
-use project_model::{ProjectWorkspace, WorkspaceBuildScripts};
+use project_model::{PackageRoot, ProjectWorkspace, WorkspaceBuildScripts};
 use syntax::SmolStr;
 use vfs::{file_set::FileSetConfig, AbsPath, AbsPathBuf, ChangeKind};
 
@@ -52,7 +56,8 @@ pub(crate) enum BuildDataProgress {
 
 impl GlobalState {
     pub(crate) fn is_quiescent(&self) -> bool {
-        !(self.fetch_workspaces_queue.op_in_progress()
+        !(self.last_reported_status.is_none()
+            || self.fetch_workspaces_queue.op_in_progress()
             || self.fetch_build_data_queue.op_in_progress()
             || self.vfs_progress_config_version < self.vfs_config_version
             || self.vfs_progress_n_done < self.vfs_progress_n_total)
@@ -104,9 +109,9 @@ impl GlobalState {
             status.message = Some("Workspace reload required".to_string())
         }
 
-        if let Err(error) = self.fetch_workspace_error() {
+        if let Err(_) = self.fetch_workspace_error() {
             status.health = lsp_ext::Health::Error;
-            status.message = Some(error)
+            status.message = Some("Failed to load workspaces".to_string())
         }
 
         if self.config.linked_projects().is_empty()
@@ -114,8 +119,9 @@ impl GlobalState {
             && self.config.notifications().cargo_toml_not_found
         {
             status.health = lsp_ext::Health::Warning;
-            status.message = Some("Workspace reload required".to_string())
+            status.message = Some("Failed to discover workspace".to_string())
         }
+
         status
     }
 
@@ -197,8 +203,7 @@ impl GlobalState {
         let _p = profile::span("GlobalState::switch_workspaces");
         tracing::info!(%cause, "will switch workspaces");
 
-        if let Err(error_message) = self.fetch_workspace_error() {
-            self.show_and_log_error(error_message, None);
+        if let Err(_) = self.fetch_workspace_error() {
             if !self.workspaces.is_empty() {
                 // It only makes sense to switch to a partially broken workspace
                 // if we don't have any workspace at all yet.
@@ -206,10 +211,6 @@ impl GlobalState {
             }
         }
 
-        if let Err(error) = self.fetch_build_data_error() {
-            self.show_and_log_error("failed to run build scripts".to_string(), Some(error));
-        }
-
         let Some(workspaces) = self.fetch_workspaces_queue.last_op_result() else { return; };
         let workspaces =
             workspaces.iter().filter_map(|res| res.as_ref().ok().cloned()).collect::<Vec<_>>();
@@ -388,7 +389,7 @@ impl GlobalState {
         tracing::info!("did switch workspaces");
     }
 
-    fn fetch_workspace_error(&self) -> Result<(), String> {
+    pub(super) fn fetch_workspace_error(&self) -> Result<(), String> {
         let mut buf = String::new();
 
         let Some(last_op_result) = self.fetch_workspaces_queue.last_op_result() else { return Ok(()) };
@@ -409,7 +410,7 @@ impl GlobalState {
         Err(buf)
     }
 
-    fn fetch_build_data_error(&self) -> Result<(), String> {
+    pub(super) fn fetch_build_data_error(&self) -> Result<(), String> {
         let mut buf = String::new();
 
         for ws in &self.fetch_build_data_queue.last_op_result().1 {
@@ -494,7 +495,69 @@ impl ProjectFolders {
         let mut fsc = FileSetConfig::builder();
         let mut local_filesets = vec![];
 
-        for root in workspaces.iter().flat_map(|ws| ws.to_roots()) {
+        // Dedup source roots
+        // Depending on the project setup, we can have duplicated source roots, or for example in
+        // the case of the rustc workspace, we can end up with two source roots that are almost the
+        // same but not quite, like:
+        // PackageRoot { is_local: false, include: [AbsPathBuf(".../rust/src/tools/miri/cargo-miri")], exclude: [] }
+        // PackageRoot {
+        //     is_local: true,
+        //     include: [AbsPathBuf(".../rust/src/tools/miri/cargo-miri"), AbsPathBuf(".../rust/build/x86_64-pc-windows-msvc/stage0-tools/x86_64-pc-windows-msvc/release/build/cargo-miri-85801cd3d2d1dae4/out")],
+        //     exclude: [AbsPathBuf(".../rust/src/tools/miri/cargo-miri/.git"), AbsPathBuf(".../rust/src/tools/miri/cargo-miri/target")]
+        // }
+        //
+        // The first one comes from the explicit rustc workspace which points to the rustc workspace itself
+        // The second comes from the rustc workspace that we load as the actual project workspace
+        // These `is_local` differing in this kind of way gives us problems, especially when trying to filter diagnostics as we don't report diagnostics for external libraries.
+        // So we need to deduplicate these, usually it would be enough to deduplicate by `include`, but as the rustc example shows here that doesn't work,
+        // so we need to also coalesce the includes if they overlap.
+
+        let mut roots: Vec<_> = workspaces
+            .iter()
+            .flat_map(|ws| ws.to_roots())
+            .update(|root| root.include.sort())
+            .sorted_by(|a, b| a.include.cmp(&b.include))
+            .collect();
+
+        // map that tracks indices of overlapping roots
+        let mut overlap_map = FxHashMap::<_, Vec<_>>::default();
+        let mut done = false;
+
+        while !mem::replace(&mut done, true) {
+            // maps include paths to indices of the corresponding root
+            let mut include_to_idx = FxHashMap::default();
+            // Find and note down the indices of overlapping roots
+            for (idx, root) in roots.iter().enumerate().filter(|(_, it)| !it.include.is_empty()) {
+                for include in &root.include {
+                    match include_to_idx.entry(include) {
+                        Entry::Occupied(e) => {
+                            overlap_map.entry(*e.get()).or_default().push(idx);
+                        }
+                        Entry::Vacant(e) => {
+                            e.insert(idx);
+                        }
+                    }
+                }
+            }
+            for (k, v) in overlap_map.drain() {
+                done = false;
+                for v in v {
+                    let r = mem::replace(
+                        &mut roots[v],
+                        PackageRoot { is_local: false, include: vec![], exclude: vec![] },
+                    );
+                    roots[k].is_local |= r.is_local;
+                    roots[k].include.extend(r.include);
+                    roots[k].exclude.extend(r.exclude);
+                }
+                roots[k].include.sort();
+                roots[k].exclude.sort();
+                roots[k].include.dedup();
+                roots[k].exclude.dedup();
+            }
+        }
+
+        for root in roots.into_iter().filter(|it| !it.include.is_empty()) {
             let file_set_roots: Vec<VfsPath> =
                 root.include.iter().cloned().map(VfsPath::from).collect();
 
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/to_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/to_proto.rs
index 92029dc1de7..7d97b69f8ea 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/to_proto.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/to_proto.rs
@@ -50,7 +50,7 @@ pub(crate) fn symbol_kind(symbol_kind: SymbolKind) -> lsp_types::SymbolKind {
         SymbolKind::Struct => lsp_types::SymbolKind::STRUCT,
         SymbolKind::Enum => lsp_types::SymbolKind::ENUM,
         SymbolKind::Variant => lsp_types::SymbolKind::ENUM_MEMBER,
-        SymbolKind::Trait => lsp_types::SymbolKind::INTERFACE,
+        SymbolKind::Trait | SymbolKind::TraitAlias => lsp_types::SymbolKind::INTERFACE,
         SymbolKind::Macro
         | SymbolKind::BuiltinAttr
         | SymbolKind::Attribute
@@ -135,6 +135,7 @@ pub(crate) fn completion_item_kind(
             SymbolKind::Static => lsp_types::CompletionItemKind::VALUE,
             SymbolKind::Struct => lsp_types::CompletionItemKind::STRUCT,
             SymbolKind::Trait => lsp_types::CompletionItemKind::INTERFACE,
+            SymbolKind::TraitAlias => lsp_types::CompletionItemKind::INTERFACE,
             SymbolKind::TypeAlias => lsp_types::CompletionItemKind::STRUCT,
             SymbolKind::TypeParam => lsp_types::CompletionItemKind::TYPE_PARAMETER,
             SymbolKind::Union => lsp_types::CompletionItemKind::STRUCT,
@@ -656,6 +657,7 @@ fn semantic_token_type_and_modifiers(
             SymbolKind::Union => semantic_tokens::UNION,
             SymbolKind::TypeAlias => semantic_tokens::TYPE_ALIAS,
             SymbolKind::Trait => semantic_tokens::INTERFACE,
+            SymbolKind::TraitAlias => semantic_tokens::INTERFACE,
             SymbolKind::Macro => semantic_tokens::MACRO,
             SymbolKind::BuiltinAttr => semantic_tokens::BUILTIN_ATTRIBUTE,
             SymbolKind::ToolModule => semantic_tokens::TOOL_MODULE,
diff --git a/src/tools/rust-analyzer/crates/syntax/Cargo.toml b/src/tools/rust-analyzer/crates/syntax/Cargo.toml
index 8fc493a23f5..305cf2d394b 100644
--- a/src/tools/rust-analyzer/crates/syntax/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/syntax/Cargo.toml
@@ -14,6 +14,7 @@ doctest = false
 
 [dependencies]
 cov-mark = "2.0.0-pre.1"
+either = "1.7.0"
 itertools = "0.10.5"
 rowan = "0.15.10"
 rustc_lexer = { version = "727.0.0", package = "rustc-ap-rustc_lexer" }
diff --git a/src/tools/rust-analyzer/crates/syntax/rust.ungram b/src/tools/rust-analyzer/crates/syntax/rust.ungram
index 36ad5fddfd0..548b5ba8b8b 100644
--- a/src/tools/rust-analyzer/crates/syntax/rust.ungram
+++ b/src/tools/rust-analyzer/crates/syntax/rust.ungram
@@ -97,6 +97,7 @@ Item =
 | Static
 | Struct
 | Trait
+| TraitAlias
 | TypeAlias
 | Union
 | Use
@@ -240,10 +241,11 @@ Trait =
   Attr* Visibility?
   'unsafe'? 'auto'?
   'trait' Name GenericParamList?
-  (
-    (':' TypeBoundList?)? WhereClause? AssocItemList
-    | '=' TypeBoundList? WhereClause? ';'
-  )
+  (':' TypeBoundList?)? WhereClause? AssocItemList
+
+TraitAlias =
+  Attr* Visibility?
+  'trait' Name GenericParamList? '=' TypeBoundList? WhereClause? ';'
 
 AssocItemList =
   '{' Attr* AssocItem* '}'
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast.rs b/src/tools/rust-analyzer/crates/syntax/src/ast.rs
index 385a4e0a3ce..1e691befff6 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast.rs
@@ -13,7 +13,7 @@ pub mod prec;
 
 use std::marker::PhantomData;
 
-use itertools::Either;
+use either::Either;
 
 use crate::{
     syntax_node::{SyntaxNode, SyntaxNodeChildren, SyntaxToken},
@@ -25,7 +25,8 @@ pub use self::{
     generated::{nodes::*, tokens::*},
     node_ext::{
         AttrKind, FieldKind, Macro, NameLike, NameOrNameRef, PathSegmentKind, SelfParamKind,
-        SlicePatComponents, StructKind, TypeBoundKind, TypeOrConstParam, VisibilityKind,
+        SlicePatComponents, StructKind, TraitOrAlias, TypeBoundKind, TypeOrConstParam,
+        VisibilityKind,
     },
     operators::{ArithOp, BinaryOp, CmpOp, LogicOp, Ordering, RangeOp, UnaryOp},
     token_ext::{CommentKind, CommentPlacement, CommentShape, IsString, QuoteOffsets, Radix},
@@ -128,6 +129,13 @@ where
     }
 }
 
+impl<L, R> HasAttrs for Either<L, R>
+where
+    L: HasAttrs,
+    R: HasAttrs,
+{
+}
+
 mod support {
     use super::{AstChildren, AstNode, SyntaxKind, SyntaxNode, SyntaxToken};
 
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs
index 642a3bfc35d..fe324845360 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs
@@ -407,7 +407,21 @@ impl Trait {
     pub fn auto_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![auto]) }
     pub fn trait_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![trait]) }
     pub fn assoc_item_list(&self) -> Option<AssocItemList> { support::child(&self.syntax) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct TraitAlias {
+    pub(crate) syntax: SyntaxNode,
+}
+impl ast::HasAttrs for TraitAlias {}
+impl ast::HasName for TraitAlias {}
+impl ast::HasVisibility for TraitAlias {}
+impl ast::HasGenericParams for TraitAlias {}
+impl ast::HasDocComments for TraitAlias {}
+impl TraitAlias {
+    pub fn trait_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![trait]) }
     pub fn eq_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) }
+    pub fn type_bound_list(&self) -> Option<TypeBoundList> { support::child(&self.syntax) }
     pub fn semicolon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![;]) }
 }
 
@@ -1573,6 +1587,7 @@ pub enum Item {
     Static(Static),
     Struct(Struct),
     Trait(Trait),
+    TraitAlias(TraitAlias),
     TypeAlias(TypeAlias),
     Union(Union),
     Use(Use),
@@ -2058,6 +2073,17 @@ impl AstNode for Trait {
     }
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
+impl AstNode for TraitAlias {
+    fn can_cast(kind: SyntaxKind) -> bool { kind == TRAIT_ALIAS }
+    fn cast(syntax: SyntaxNode) -> Option<Self> {
+        if Self::can_cast(syntax.kind()) {
+            Some(Self { syntax })
+        } else {
+            None
+        }
+    }
+    fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
 impl AstNode for TypeAlias {
     fn can_cast(kind: SyntaxKind) -> bool { kind == TYPE_ALIAS }
     fn cast(syntax: SyntaxNode) -> Option<Self> {
@@ -3570,6 +3596,9 @@ impl From<Struct> for Item {
 impl From<Trait> for Item {
     fn from(node: Trait) -> Item { Item::Trait(node) }
 }
+impl From<TraitAlias> for Item {
+    fn from(node: TraitAlias) -> Item { Item::TraitAlias(node) }
+}
 impl From<TypeAlias> for Item {
     fn from(node: TypeAlias) -> Item { Item::TypeAlias(node) }
 }
@@ -3596,6 +3625,7 @@ impl AstNode for Item {
                 | STATIC
                 | STRUCT
                 | TRAIT
+                | TRAIT_ALIAS
                 | TYPE_ALIAS
                 | UNION
                 | USE
@@ -3616,6 +3646,7 @@ impl AstNode for Item {
             STATIC => Item::Static(Static { syntax }),
             STRUCT => Item::Struct(Struct { syntax }),
             TRAIT => Item::Trait(Trait { syntax }),
+            TRAIT_ALIAS => Item::TraitAlias(TraitAlias { syntax }),
             TYPE_ALIAS => Item::TypeAlias(TypeAlias { syntax }),
             UNION => Item::Union(Union { syntax }),
             USE => Item::Use(Use { syntax }),
@@ -3638,6 +3669,7 @@ impl AstNode for Item {
             Item::Static(it) => &it.syntax,
             Item::Struct(it) => &it.syntax,
             Item::Trait(it) => &it.syntax,
+            Item::TraitAlias(it) => &it.syntax,
             Item::TypeAlias(it) => &it.syntax,
             Item::Union(it) => &it.syntax,
             Item::Use(it) => &it.syntax,
@@ -3950,6 +3982,7 @@ impl AstNode for AnyHasAttrs {
                 | STATIC
                 | STRUCT
                 | TRAIT
+                | TRAIT_ALIAS
                 | TYPE_ALIAS
                 | UNION
                 | USE
@@ -4035,6 +4068,7 @@ impl AstNode for AnyHasDocComments {
                 | STATIC
                 | STRUCT
                 | TRAIT
+                | TRAIT_ALIAS
                 | TYPE_ALIAS
                 | UNION
                 | USE
@@ -4056,7 +4090,7 @@ impl AnyHasGenericParams {
 }
 impl AstNode for AnyHasGenericParams {
     fn can_cast(kind: SyntaxKind) -> bool {
-        matches!(kind, ENUM | FN | IMPL | STRUCT | TRAIT | TYPE_ALIAS | UNION)
+        matches!(kind, ENUM | FN | IMPL | STRUCT | TRAIT | TRAIT_ALIAS | TYPE_ALIAS | UNION)
     }
     fn cast(syntax: SyntaxNode) -> Option<Self> {
         Self::can_cast(syntax.kind()).then_some(AnyHasGenericParams { syntax })
@@ -4108,6 +4142,7 @@ impl AstNode for AnyHasName {
                 | STATIC
                 | STRUCT
                 | TRAIT
+                | TRAIT_ALIAS
                 | TYPE_ALIAS
                 | UNION
                 | RENAME
@@ -4163,6 +4198,7 @@ impl AstNode for AnyHasVisibility {
                 | STATIC
                 | STRUCT
                 | TRAIT
+                | TRAIT_ALIAS
                 | TYPE_ALIAS
                 | UNION
                 | USE
@@ -4391,6 +4427,11 @@ impl std::fmt::Display for Trait {
         std::fmt::Display::fmt(self.syntax(), f)
     }
 }
+impl std::fmt::Display for TraitAlias {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        std::fmt::Display::fmt(self.syntax(), f)
+    }
+}
 impl std::fmt::Display for TypeAlias {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         std::fmt::Display::fmt(self.syntax(), f)
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs
index fe82aa90722..15bd5ab3c72 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs
@@ -680,6 +680,81 @@ impl TypeOrConstParam {
     }
 }
 
+impl AstNode for TypeOrConstParam {
+    fn can_cast(kind: SyntaxKind) -> bool
+    where
+        Self: Sized,
+    {
+        matches!(kind, SyntaxKind::TYPE_PARAM | SyntaxKind::CONST_PARAM)
+    }
+
+    fn cast(syntax: SyntaxNode) -> Option<Self>
+    where
+        Self: Sized,
+    {
+        let res = match syntax.kind() {
+            SyntaxKind::TYPE_PARAM => TypeOrConstParam::Type(ast::TypeParam { syntax }),
+            SyntaxKind::CONST_PARAM => TypeOrConstParam::Const(ast::ConstParam { syntax }),
+            _ => return None,
+        };
+        Some(res)
+    }
+
+    fn syntax(&self) -> &SyntaxNode {
+        match self {
+            TypeOrConstParam::Type(it) => it.syntax(),
+            TypeOrConstParam::Const(it) => it.syntax(),
+        }
+    }
+}
+
+impl HasAttrs for TypeOrConstParam {}
+
+#[derive(Debug, Clone)]
+pub enum TraitOrAlias {
+    Trait(ast::Trait),
+    TraitAlias(ast::TraitAlias),
+}
+
+impl TraitOrAlias {
+    pub fn name(&self) -> Option<ast::Name> {
+        match self {
+            TraitOrAlias::Trait(x) => x.name(),
+            TraitOrAlias::TraitAlias(x) => x.name(),
+        }
+    }
+}
+
+impl AstNode for TraitOrAlias {
+    fn can_cast(kind: SyntaxKind) -> bool
+    where
+        Self: Sized,
+    {
+        matches!(kind, SyntaxKind::TRAIT | SyntaxKind::TRAIT_ALIAS)
+    }
+
+    fn cast(syntax: SyntaxNode) -> Option<Self>
+    where
+        Self: Sized,
+    {
+        let res = match syntax.kind() {
+            SyntaxKind::TRAIT => TraitOrAlias::Trait(ast::Trait { syntax }),
+            SyntaxKind::TRAIT_ALIAS => TraitOrAlias::TraitAlias(ast::TraitAlias { syntax }),
+            _ => return None,
+        };
+        Some(res)
+    }
+
+    fn syntax(&self) -> &SyntaxNode {
+        match self {
+            TraitOrAlias::Trait(it) => it.syntax(),
+            TraitOrAlias::TraitAlias(it) => it.syntax(),
+        }
+    }
+}
+
+impl HasAttrs for TraitOrAlias {}
+
 pub enum VisibilityKind {
     In(ast::Path),
     PubCrate,
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/traits.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/traits.rs
index aa2b7ed5c8b..3e43df2d0d5 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/traits.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/traits.rs
@@ -1,7 +1,7 @@
 //! Various traits that are implemented by ast nodes.
 //!
 //! The implementations are usually trivial, and live in generated.rs
-use itertools::Either;
+use either::Either;
 
 use crate::{
     ast::{self, support, AstChildren, AstNode, AstToken},
@@ -134,3 +134,5 @@ impl Iterator for AttrDocCommentIter {
         })
     }
 }
+
+impl<A: HasName, B: HasName> HasName for Either<A, B> {}
diff --git a/src/tools/rust-analyzer/crates/syntax/src/tests/ast_src.rs b/src/tools/rust-analyzer/crates/syntax/src/tests/ast_src.rs
index 3ff6e03006b..ccce71966ff 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/tests/ast_src.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/tests/ast_src.rs
@@ -86,6 +86,7 @@ pub(crate) const KINDS_SRC: KindsSrc<'_> = KindsSrc {
         "STATIC",
         "CONST",
         "TRAIT",
+        "TRAIT_ALIAS",
         "IMPL",
         "TYPE_ALIAS",
         "MACRO_CALL",
diff --git a/src/tools/rust-analyzer/crates/syntax/src/tests/sourcegen_ast.rs b/src/tools/rust-analyzer/crates/syntax/src/tests/sourcegen_ast.rs
index 03aa2c451e8..e954b58251f 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/tests/sourcegen_ast.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/tests/sourcegen_ast.rs
@@ -783,6 +783,7 @@ fn extract_struct_traits(ast: &mut AstSrc) {
         "Enum",
         "Variant",
         "Trait",
+        "TraitAlias",
         "Module",
         "Static",
         "Const",
diff --git a/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs b/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs
index d1afd0039aa..cd1235fa6dc 100644
--- a/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs
+++ b/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs
@@ -180,7 +180,9 @@ impl Fixture {
         let mut cfg_key_values = Vec::new();
         let mut env = FxHashMap::default();
         let mut introduce_new_source_root = None;
-        let mut target_data_layout = None;
+        let mut target_data_layout = Some(
+            "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128".to_string(),
+        );
         for component in components[1..].iter() {
             let (key, value) =
                 component.split_once(':').unwrap_or_else(|| panic!("invalid meta line: {meta:?}"));
diff --git a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs
index 3b033e1aae5..93ff76a040c 100644
--- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs
+++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs
@@ -510,6 +510,7 @@ pub mod fmt {
 pub mod slice {
     #[lang = "slice"]
     impl<T> [T] {
+        #[lang = "slice_len_fn"]
         pub fn len(&self) -> usize {
             loop {}
         }
@@ -533,6 +534,40 @@ pub mod option {
                 None => panic!("called `Option::unwrap()` on a `None` value"),
             }
         }
+
+        pub fn and<U>(self, optb: Option<U>) -> Option<U> {
+            loop {}
+        }
+        pub fn unwrap_or(self, default: T) -> T {
+            loop {}
+        }
+        // region:fn
+        pub fn and_then<U, F>(self, f: F) -> Option<U>
+        where
+            F: FnOnce(T) -> Option<U>,
+        {
+            loop {}
+        }
+        pub fn unwrap_or_else<F>(self, f: F) -> T
+        where
+            F: FnOnce() -> T,
+        {
+            loop {}
+        }
+        pub fn map_or<U, F>(self, default: U, f: F) -> U
+        where
+            F: FnOnce(T) -> U,
+        {
+            loop {}
+        }
+        pub fn map_or_else<U, D, F>(self, default: D, f: F) -> U
+        where
+            D: FnOnce() -> U,
+            F: FnOnce(T) -> U,
+        {
+            loop {}
+        }
+        // endregion:fn
     }
 }
 // endregion:option
@@ -727,6 +762,20 @@ pub mod iter {
                     self
                 }
             }
+            pub struct IntoIter<T, const N: usize>([T; N]);
+            impl<T, const N: usize> IntoIterator for [T; N] {
+                type Item = T;
+                type IntoIter = IntoIter<T, N>;
+                fn into_iter(self) -> I {
+                    IntoIter(self)
+                }
+            }
+            impl<T, const N: usize> Iterator for IntoIter<T, N> {
+                type Item = T;
+                fn next(&mut self) -> Option<T> {
+                    loop {}
+                }
+            }
         }
         pub use self::collect::IntoIterator;
     }
diff --git a/src/tools/rust-analyzer/crates/toolchain/src/lib.rs b/src/tools/rust-analyzer/crates/toolchain/src/lib.rs
index 67bdad2aadd..729f84a8150 100644
--- a/src/tools/rust-analyzer/crates/toolchain/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/toolchain/src/lib.rs
@@ -31,8 +31,9 @@ fn get_path_for_executable(executable_name: &'static str) -> PathBuf {
     //      example: for cargo, this checks $CARGO environment variable; for rustc, $RUSTC; etc
     // 2) `<executable_name>`
     //      example: for cargo, this tries just `cargo`, which will succeed if `cargo` is on the $PATH
-    // 3) `~/.cargo/bin/<executable_name>`
-    //      example: for cargo, this tries ~/.cargo/bin/cargo
+    // 3) `$CARGO_HOME/bin/<executable_name>`
+    //      where $CARGO_HOME defaults to ~/.cargo (see https://doc.rust-lang.org/cargo/guide/cargo-home.html)
+    //      example: for cargo, this tries $CARGO_HOME/bin/cargo, or ~/.cargo/bin/cargo if $CARGO_HOME is unset.
     //      It seems that this is a reasonable place to try for cargo, rustc, and rustup
     let env_var = executable_name.to_ascii_uppercase();
     if let Some(path) = env::var_os(env_var) {
@@ -43,8 +44,7 @@ fn get_path_for_executable(executable_name: &'static str) -> PathBuf {
         return executable_name.into();
     }
 
-    if let Some(mut path) = home::home_dir() {
-        path.push(".cargo");
+    if let Some(mut path) = get_cargo_home() {
         path.push("bin");
         path.push(executable_name);
         if let Some(path) = probe(path) {
@@ -60,6 +60,19 @@ fn lookup_in_path(exec: &str) -> bool {
     env::split_paths(&paths).map(|path| path.join(exec)).find_map(probe).is_some()
 }
 
+fn get_cargo_home() -> Option<PathBuf> {
+    if let Some(path) = env::var_os("CARGO_HOME") {
+        return Some(path.into());
+    }
+
+    if let Some(mut path) = home::home_dir() {
+        path.push(".cargo");
+        return Some(path);
+    }
+
+    None
+}
+
 fn probe(path: PathBuf) -> Option<PathBuf> {
     let with_extension = match env::consts::EXE_EXTENSION {
         "" => None,
diff --git a/src/tools/rust-analyzer/docs/dev/lsp-extensions.md b/src/tools/rust-analyzer/docs/dev/lsp-extensions.md
index c3623a5cc46..de142203208 100644
--- a/src/tools/rust-analyzer/docs/dev/lsp-extensions.md
+++ b/src/tools/rust-analyzer/docs/dev/lsp-extensions.md
@@ -1,5 +1,5 @@
 <!---
-lsp_ext.rs hash: d87477896dfe41d4
+lsp_ext.rs hash: 37f31ae648632897
 
 If you need to change the above hash to make the test pass, please check if you
 need to adjust this doc as well and ping this issue:
@@ -527,6 +527,17 @@ Primarily for debugging, but very useful for all people working on rust-analyzer
 Returns a textual representation of the HIR of the function containing the cursor.
 For debugging or when working on rust-analyzer itself.
 
+## View Mir
+
+**Method:** `rust-analyzer/viewMir`
+
+**Request:** `TextDocumentPositionParams`
+
+**Response:** `string`
+
+Returns a textual representation of the MIR of the function containing the cursor.
+For debugging or when working on rust-analyzer itself.
+
 ## View File Text
 
 **Method:** `rust-analyzer/viewFileText`
diff --git a/src/tools/rust-analyzer/docs/user/generated_config.adoc b/src/tools/rust-analyzer/docs/user/generated_config.adoc
index 50e3670a7a8..6937a7ed9a2 100644
--- a/src/tools/rust-analyzer/docs/user/generated_config.adoc
+++ b/src/tools/rust-analyzer/docs/user/generated_config.adoc
@@ -71,6 +71,11 @@ cargo check --quiet --workspace --message-format=json --all-targets
 Use `RUSTC_WRAPPER=rust-analyzer` when running build scripts to
 avoid checking unnecessary things.
 --
+[[rust-analyzer.cargo.extraArgs]]rust-analyzer.cargo.extraArgs (default: `[]`)::
++
+--
+Extra arguments that are passed to every cargo invocation.
+--
 [[rust-analyzer.cargo.extraEnv]]rust-analyzer.cargo.extraEnv (default: `{}`)::
 +
 --
@@ -537,6 +542,11 @@ Only applies to closures with blocks, same as `#rust-analyzer.inlayHints.closure
 --
 Whether to hide inlay type hints for constructors.
 --
+[[rust-analyzer.interpret.tests]]rust-analyzer.interpret.tests (default: `false`)::
++
+--
+Enables the experimental support for interpreting tests.
+--
 [[rust-analyzer.joinLines.joinAssignments]]rust-analyzer.joinLines.joinAssignments (default: `true`)::
 +
 --
@@ -699,7 +709,10 @@ Additional arguments to `rustfmt`.
 +
 --
 Advanced option, fully override the command rust-analyzer uses for
-formatting.
+formatting. This should be the equivalent of `rustfmt` here, and
+not that of `cargo fmt`. The file contents will be passed on the
+standard input and the formatted result will be read from the
+standard output.
 --
 [[rust-analyzer.rustfmt.rangeFormatting.enable]]rust-analyzer.rustfmt.rangeFormatting.enable (default: `false`)::
 +
diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json
index 3610e993f82..a3b1a3107d0 100644
--- a/src/tools/rust-analyzer/editors/code/package.json
+++ b/src/tools/rust-analyzer/editors/code/package.json
@@ -115,6 +115,11 @@
                 "category": "rust-analyzer (debug command)"
             },
             {
+                "command": "rust-analyzer.viewMir",
+                "title": "View Mir",
+                "category": "rust-analyzer (debug command)"
+            },
+            {
                 "command": "rust-analyzer.viewFileText",
                 "title": "View File Text (as seen by the server)",
                 "category": "rust-analyzer (debug command)"
@@ -226,7 +231,7 @@
             },
             {
                 "command": "rust-analyzer.openDocs",
-                "title": "Open docs under cursor",
+                "title": "Open Docs",
                 "category": "rust-analyzer"
             },
             {
@@ -236,7 +241,7 @@
             },
             {
                 "command": "rust-analyzer.peekTests",
-                "title": "Peek related tests",
+                "title": "Peek Related Tests",
                 "category": "rust-analyzer"
             },
             {
@@ -506,6 +511,14 @@
                     "default": true,
                     "type": "boolean"
                 },
+                "rust-analyzer.cargo.extraArgs": {
+                    "markdownDescription": "Extra arguments that are passed to every cargo invocation.",
+                    "default": [],
+                    "type": "array",
+                    "items": {
+                        "type": "string"
+                    }
+                },
                 "rust-analyzer.cargo.extraEnv": {
                     "markdownDescription": "Extra environment variables that will be set when running cargo, rustc\nor other commands within the workspace. Useful for setting RUSTFLAGS.",
                     "default": {},
@@ -1110,6 +1123,11 @@
                     "default": false,
                     "type": "boolean"
                 },
+                "rust-analyzer.interpret.tests": {
+                    "markdownDescription": "Enables the experimental support for interpreting tests.",
+                    "default": false,
+                    "type": "boolean"
+                },
                 "rust-analyzer.joinLines.joinAssignments": {
                     "markdownDescription": "Join lines merges consecutive declaration and initialization of an assignment.",
                     "default": true,
@@ -1283,7 +1301,7 @@
                     }
                 },
                 "rust-analyzer.rustfmt.overrideCommand": {
-                    "markdownDescription": "Advanced option, fully override the command rust-analyzer uses for\nformatting.",
+                    "markdownDescription": "Advanced option, fully override the command rust-analyzer uses for\nformatting. This should be the equivalent of `rustfmt` here, and\nnot that of `cargo fmt`. The file contents will be passed on the\nstandard input and the formatted result will be read from the\nstandard output.",
                     "default": null,
                     "type": [
                         "null",
@@ -1869,8 +1887,13 @@
             "editor/context": [
                 {
                     "command": "rust-analyzer.peekTests",
-                    "when": "inRustProject",
+                    "when": "inRustProject && editorTextFocus && editorLangId == rust",
                     "group": "navigation@1000"
+                },
+                {
+                    "command": "rust-analyzer.openDocs",
+                    "when": "inRustProject && editorTextFocus && editorLangId == rust",
+                    "group": "navigation@1001"
                 }
             ]
         },
diff --git a/src/tools/rust-analyzer/editors/code/src/commands.ts b/src/tools/rust-analyzer/editors/code/src/commands.ts
index 49a8ca4edba..f4a4579a92c 100644
--- a/src/tools/rust-analyzer/editors/code/src/commands.ts
+++ b/src/tools/rust-analyzer/editors/code/src/commands.ts
@@ -93,6 +93,14 @@ export function triggerParameterHints(_: CtxInit): Cmd {
     };
 }
 
+export function openLogs(ctx: CtxInit): Cmd {
+    return async () => {
+        if (ctx.client.outputChannel) {
+            ctx.client.outputChannel.show();
+        }
+    };
+}
+
 export function matchingBrace(ctx: CtxInit): Cmd {
     return async () => {
         const editor = ctx.activeRustEditor;
@@ -405,12 +413,11 @@ export function syntaxTree(ctx: CtxInit): Cmd {
     };
 }
 
-// Opens the virtual file that will show the HIR of the function containing the cursor position
-//
-// The contents of the file come from the `TextDocumentContentProvider`
-export function viewHir(ctx: CtxInit): Cmd {
+function viewHirOrMir(ctx: CtxInit, xir: "hir" | "mir"): Cmd {
+    const viewXir = xir === "hir" ? "viewHir" : "viewMir";
+    const requestType = xir === "hir" ? ra.viewHir : ra.viewMir;
     const tdcp = new (class implements vscode.TextDocumentContentProvider {
-        readonly uri = vscode.Uri.parse("rust-analyzer-hir://viewHir/hir.rs");
+        readonly uri = vscode.Uri.parse(`rust-analyzer-${xir}://${viewXir}/${xir}.rs`);
         readonly eventEmitter = new vscode.EventEmitter<vscode.Uri>();
         constructor() {
             vscode.workspace.onDidChangeTextDocument(
@@ -452,7 +459,7 @@ export function viewHir(ctx: CtxInit): Cmd {
                 ),
                 position: client.code2ProtocolConverter.asPosition(rustEditor.selection.active),
             };
-            return client.sendRequest(ra.viewHir, params, ct);
+            return client.sendRequest(requestType, params, ct);
         }
 
         get onDidChange(): vscode.Event<vscode.Uri> {
@@ -461,7 +468,7 @@ export function viewHir(ctx: CtxInit): Cmd {
     })();
 
     ctx.pushExtCleanup(
-        vscode.workspace.registerTextDocumentContentProvider("rust-analyzer-hir", tdcp)
+        vscode.workspace.registerTextDocumentContentProvider(`rust-analyzer-${xir}`, tdcp)
     );
 
     return async () => {
@@ -474,6 +481,20 @@ export function viewHir(ctx: CtxInit): Cmd {
     };
 }
 
+// Opens the virtual file that will show the HIR of the function containing the cursor position
+//
+// The contents of the file come from the `TextDocumentContentProvider`
+export function viewHir(ctx: CtxInit): Cmd {
+    return viewHirOrMir(ctx, "hir");
+}
+
+// Opens the virtual file that will show the MIR of the function containing the cursor position
+//
+// The contents of the file come from the `TextDocumentContentProvider`
+export function viewMir(ctx: CtxInit): Cmd {
+    return viewHirOrMir(ctx, "mir");
+}
+
 export function viewFileText(ctx: CtxInit): Cmd {
     const tdcp = new (class implements vscode.TextDocumentContentProvider {
         readonly uri = vscode.Uri.parse("rust-analyzer-file-text://viewFileText/file.rs");
diff --git a/src/tools/rust-analyzer/editors/code/src/ctx.ts b/src/tools/rust-analyzer/editors/code/src/ctx.ts
index e2a30e0cc45..1708d47cee7 100644
--- a/src/tools/rust-analyzer/editors/code/src/ctx.ts
+++ b/src/tools/rust-analyzer/editors/code/src/ctx.ts
@@ -282,18 +282,18 @@ export class Ctx {
     setServerStatus(status: ServerStatusParams | { health: "stopped" }) {
         let icon = "";
         const statusBar = this.statusBar;
+        statusBar.tooltip = new vscode.MarkdownString("", true);
+        statusBar.tooltip.isTrusted = true;
         switch (status.health) {
             case "ok":
-                statusBar.tooltip = (status.message ?? "Ready") + "\nClick to stop server.";
-                statusBar.command = "rust-analyzer.stopServer";
+                statusBar.tooltip.appendText(status.message ?? "Ready");
                 statusBar.color = undefined;
                 statusBar.backgroundColor = undefined;
                 break;
             case "warning":
-                statusBar.tooltip =
-                    (status.message ? status.message + "\n" : "") + "Click to reload.";
-
-                statusBar.command = "rust-analyzer.reloadWorkspace";
+                if (status.message) {
+                    statusBar.tooltip.appendText(status.message);
+                }
                 statusBar.color = new vscode.ThemeColor("statusBarItem.warningForeground");
                 statusBar.backgroundColor = new vscode.ThemeColor(
                     "statusBarItem.warningBackground"
@@ -301,22 +301,32 @@ export class Ctx {
                 icon = "$(warning) ";
                 break;
             case "error":
-                statusBar.tooltip =
-                    (status.message ? status.message + "\n" : "") + "Click to reload.";
-
-                statusBar.command = "rust-analyzer.reloadWorkspace";
+                if (status.message) {
+                    statusBar.tooltip.appendText(status.message);
+                }
                 statusBar.color = new vscode.ThemeColor("statusBarItem.errorForeground");
                 statusBar.backgroundColor = new vscode.ThemeColor("statusBarItem.errorBackground");
                 icon = "$(error) ";
                 break;
             case "stopped":
-                statusBar.tooltip = "Server is stopped.\nClick to start.";
-                statusBar.command = "rust-analyzer.startServer";
+                statusBar.tooltip.appendText("Server is stopped");
+                statusBar.tooltip.appendMarkdown(
+                    "\n\n[Start server](command:rust-analyzer.startServer)"
+                );
                 statusBar.color = undefined;
                 statusBar.backgroundColor = undefined;
                 statusBar.text = `$(stop-circle) rust-analyzer`;
                 return;
         }
+        if (statusBar.tooltip.value) {
+            statusBar.tooltip.appendText("\n\n");
+        }
+        statusBar.tooltip.appendMarkdown("[Stop server](command:rust-analyzer.stopServer)");
+        statusBar.tooltip.appendMarkdown(
+            "\n\n[Reload Workspace](command:rust-analyzer.reloadWorkspace)"
+        );
+        statusBar.tooltip.appendMarkdown("\n\n[Restart server](command:rust-analyzer.startServer)");
+        statusBar.tooltip.appendMarkdown("\n\n[Open logs](command:rust-analyzer.openLogs)");
         if (!status.quiescent) icon = "$(sync~spin) ";
         statusBar.text = `${icon}rust-analyzer`;
     }
diff --git a/src/tools/rust-analyzer/editors/code/src/lsp_ext.ts b/src/tools/rust-analyzer/editors/code/src/lsp_ext.ts
index f6f5124dc41..400cd207d41 100644
--- a/src/tools/rust-analyzer/editors/code/src/lsp_ext.ts
+++ b/src/tools/rust-analyzer/editors/code/src/lsp_ext.ts
@@ -59,6 +59,9 @@ export const viewFileText = new lc.RequestType<lc.TextDocumentIdentifier, string
 export const viewHir = new lc.RequestType<lc.TextDocumentPositionParams, string, void>(
     "rust-analyzer/viewHir"
 );
+export const viewMir = new lc.RequestType<lc.TextDocumentPositionParams, string, void>(
+    "rust-analyzer/viewMir"
+);
 export const viewItemTree = new lc.RequestType<ViewItemTreeParams, string, void>(
     "rust-analyzer/viewItemTree"
 );
diff --git a/src/tools/rust-analyzer/editors/code/src/main.ts b/src/tools/rust-analyzer/editors/code/src/main.ts
index 5987368e6e0..8a2412af849 100644
--- a/src/tools/rust-analyzer/editors/code/src/main.ts
+++ b/src/tools/rust-analyzer/editors/code/src/main.ts
@@ -158,6 +158,7 @@ function createCommands(): Record<string, CommandFactory> {
         parentModule: { enabled: commands.parentModule },
         syntaxTree: { enabled: commands.syntaxTree },
         viewHir: { enabled: commands.viewHir },
+        viewMir: { enabled: commands.viewMir },
         viewFileText: { enabled: commands.viewFileText },
         viewItemTree: { enabled: commands.viewItemTree },
         viewCrateGraph: { enabled: commands.viewCrateGraph },
@@ -187,5 +188,6 @@ function createCommands(): Record<string, CommandFactory> {
         runSingle: { enabled: commands.runSingle },
         showReferences: { enabled: commands.showReferences },
         triggerParameterHints: { enabled: commands.triggerParameterHints },
+        openLogs: { enabled: commands.openLogs },
     };
 }
diff --git a/src/tools/rust-analyzer/editors/code/src/toolchain.ts b/src/tools/rust-analyzer/editors/code/src/toolchain.ts
index e1ca4954280..eb70b88871e 100644
--- a/src/tools/rust-analyzer/editors/code/src/toolchain.ts
+++ b/src/tools/rust-analyzer/editors/code/src/toolchain.ts
@@ -156,19 +156,10 @@ export const getPathForExecutable = memoizeAsync(
 
         if (await lookupInPath(executableName)) return executableName;
 
-        try {
-            // hmm, `os.homedir()` seems to be infallible
-            // it is not mentioned in docs and cannot be inferred by the type signature...
-            const standardPath = vscode.Uri.joinPath(
-                vscode.Uri.file(os.homedir()),
-                ".cargo",
-                "bin",
-                executableName
-            );
-
+        const cargoHome = getCargoHome();
+        if (cargoHome) {
+            const standardPath = vscode.Uri.joinPath(cargoHome, "bin", executableName);
             if (await isFileAtUri(standardPath)) return standardPath.fsPath;
-        } catch (err) {
-            log.error("Failed to read the fs info", err);
         }
         return executableName;
     }
@@ -190,6 +181,21 @@ async function lookupInPath(exec: string): Promise<boolean> {
     return false;
 }
 
+function getCargoHome(): vscode.Uri | null {
+    const envVar = process.env["CARGO_HOME"];
+    if (envVar) return vscode.Uri.file(envVar);
+
+    try {
+        // hmm, `os.homedir()` seems to be infallible
+        // it is not mentioned in docs and cannot be inferred by the type signature...
+        return vscode.Uri.joinPath(vscode.Uri.file(os.homedir()), ".cargo");
+    } catch (err) {
+        log.error("Failed to read the fs info", err);
+    }
+
+    return null;
+}
+
 async function isFileAtPath(path: string): Promise<boolean> {
     return isFileAtUri(vscode.Uri.file(path));
 }
diff --git a/src/tools/rust-analyzer/lib/la-arena/src/map.rs b/src/tools/rust-analyzer/lib/la-arena/src/map.rs
index b9d491da3c0..7fff2b09c97 100644
--- a/src/tools/rust-analyzer/lib/la-arena/src/map.rs
+++ b/src/tools/rust-analyzer/lib/la-arena/src/map.rs
@@ -94,6 +94,12 @@ impl<T, V> ArenaMap<Idx<T>, V> {
             .filter_map(|(idx, o)| Some((Self::from_idx(idx), o.as_mut()?)))
     }
 
+    /// Returns an iterator over the arena indexes and values in the map.
+    // FIXME: Implement `IntoIterator` trait.
+    pub fn into_iter(self) -> impl Iterator<Item = (Idx<T>, V)> {
+        self.v.into_iter().enumerate().filter_map(|(idx, o)| Some((Self::from_idx(idx), o?)))
+    }
+
     /// Gets the given key's corresponding entry in the map for in-place manipulation.
     pub fn entry(&mut self, idx: Idx<T>) -> Entry<'_, Idx<T>, V> {
         let idx = Self::to_idx(idx);
diff --git a/src/tools/rust-installer/combine-installers.sh b/src/tools/rust-installer/combine-installers.sh
index bdbaab71139..bee5319fd55 100755
--- a/src/tools/rust-installer/combine-installers.sh
+++ b/src/tools/rust-installer/combine-installers.sh
@@ -1,13 +1,4 @@
 #!/bin/bash
-# Copyright 2014 The Rust Project Developers. See the COPYRIGHT
-# file at the top-level directory of this distribution and at
-# http://rust-lang.org/COPYRIGHT.
-#
-# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-# http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-# <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-# option. This file may not be copied, modified, or distributed
-# except according to those terms.
 
 set -ue
 
diff --git a/src/tools/rust-installer/gen-install-script.sh b/src/tools/rust-installer/gen-install-script.sh
index b4559d147ad..f112fd4b21f 100755
--- a/src/tools/rust-installer/gen-install-script.sh
+++ b/src/tools/rust-installer/gen-install-script.sh
@@ -1,13 +1,4 @@
 #!/bin/bash
-# Copyright 2014 The Rust Project Developers. See the COPYRIGHT
-# file at the top-level directory of this distribution and at
-# http://rust-lang.org/COPYRIGHT.
-#
-# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-# http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-# <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-# option. This file may not be copied, modified, or distributed
-# except according to those terms.
 
 set -ue
 
diff --git a/src/tools/rust-installer/gen-installer.sh b/src/tools/rust-installer/gen-installer.sh
index 9a2c3016fee..eabd8c95cd8 100755
--- a/src/tools/rust-installer/gen-installer.sh
+++ b/src/tools/rust-installer/gen-installer.sh
@@ -1,13 +1,4 @@
 #!/bin/bash
-# Copyright 2014 The Rust Project Developers. See the COPYRIGHT
-# file at the top-level directory of this distribution and at
-# http://rust-lang.org/COPYRIGHT.
-#
-# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-# http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-# <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-# option. This file may not be copied, modified, or distributed
-# except according to those terms.
 
 set -ue
 
diff --git a/src/tools/rust-installer/install-template.sh b/src/tools/rust-installer/install-template.sh
index 7790541a420..92a3f1f2c98 100644
--- a/src/tools/rust-installer/install-template.sh
+++ b/src/tools/rust-installer/install-template.sh
@@ -1,13 +1,4 @@
 #!/bin/bash
-# Copyright 2014 The Rust Project Developers. See the COPYRIGHT
-# file at the top-level directory of this distribution and at
-# http://rust-lang.org/COPYRIGHT.
-#
-# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-# http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-# <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-# option. This file may not be copied, modified, or distributed
-# except according to those terms.
 
 # No undefined variables
 set -u
diff --git a/src/tools/rust-installer/make-tarballs.sh b/src/tools/rust-installer/make-tarballs.sh
index 6fc823666f1..e342007da37 100755
--- a/src/tools/rust-installer/make-tarballs.sh
+++ b/src/tools/rust-installer/make-tarballs.sh
@@ -1,13 +1,4 @@
 #!/bin/sh
-# Copyright 2014 The Rust Project Developers. See the COPYRIGHT
-# file at the top-level directory of this distribution and at
-# http://rust-lang.org/COPYRIGHT.
-#
-# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-# http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-# <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-# option. This file may not be copied, modified, or distributed
-# except according to those terms.
 
 set -ue
 
diff --git a/src/tools/rustdoc-gui/.eslintrc.js b/src/tools/rustdoc-gui/.eslintrc.js
new file mode 100644
index 00000000000..f4aadc07199
--- /dev/null
+++ b/src/tools/rustdoc-gui/.eslintrc.js
@@ -0,0 +1,96 @@
+module.exports = {
+    "env": {
+        "browser": true,
+        "node": true,
+        "es6": true
+    },
+    "extends": "eslint:recommended",
+    "parserOptions": {
+        "ecmaVersion": 2018,
+        "sourceType": "module"
+    },
+    "rules": {
+        "linebreak-style": [
+            "error",
+            "unix"
+        ],
+        "semi": [
+            "error",
+            "always"
+        ],
+        "quotes": [
+            "error",
+            "double"
+        ],
+        "linebreak-style": [
+            "error",
+            "unix"
+        ],
+        "no-trailing-spaces": "error",
+        "no-var": ["error"],
+        "prefer-const": ["error"],
+        "prefer-arrow-callback": ["error"],
+        "brace-style": [
+            "error",
+            "1tbs",
+            { "allowSingleLine": false }
+        ],
+        "keyword-spacing": [
+            "error",
+            { "before": true, "after": true }
+        ],
+        "arrow-spacing": [
+            "error",
+            { "before": true, "after": true }
+        ],
+        "key-spacing": [
+            "error",
+            { "beforeColon": false, "afterColon": true, "mode": "strict" }
+        ],
+        "func-call-spacing": ["error", "never"],
+        "space-infix-ops": "error",
+        "space-before-function-paren": ["error", "never"],
+        "space-before-blocks": "error",
+        "comma-dangle": ["error", "always-multiline"],
+        "comma-style": ["error", "last"],
+        "max-len": ["error", { "code": 100, "tabWidth": 4 }],
+        "eol-last": ["error", "always"],
+        "arrow-parens": ["error", "as-needed"],
+        "no-unused-vars": [
+            "error",
+            {
+                "argsIgnorePattern": "^_",
+                "varsIgnorePattern": "^_"
+            }
+        ],
+        "eqeqeq": "error",
+        "no-const-assign": "error",
+        "no-debugger": "error",
+        "no-dupe-args": "error",
+        "no-dupe-else-if": "error",
+        "no-dupe-keys": "error",
+        "no-duplicate-case": "error",
+        "no-ex-assign": "error",
+        "no-fallthrough": "error",
+        "no-invalid-regexp": "error",
+        "no-import-assign": "error",
+        "no-self-compare": "error",
+        "no-template-curly-in-string": "error",
+        "block-scoped-var": "error",
+        "guard-for-in": "error",
+        "no-alert": "error",
+        "no-confusing-arrow": "error",
+        "no-div-regex": "error",
+        "no-floating-decimal": "error",
+        "no-implicit-globals": "error",
+        "no-implied-eval": "error",
+        "no-label-var": "error",
+        "no-lonely-if": "error",
+        "no-mixed-operators": "error",
+        "no-multi-assign": "error",
+        "no-return-assign": "error",
+        "no-script-url": "error",
+        "no-sequences": "error",
+        "no-div-regex": "error",
+    }
+};
diff --git a/src/tools/rustdoc-gui/tester.js b/src/tools/rustdoc-gui/tester.js
index 80bfd38d1e9..72baad606f0 100644
--- a/src/tools/rustdoc-gui/tester.js
+++ b/src/tools/rustdoc-gui/tester.js
@@ -6,8 +6,8 @@
 
 const fs = require("fs");
 const path = require("path");
-const os = require('os');
-const {Options, runTest} = require('browser-ui-test');
+const os = require("os");
+const {Options, runTest} = require("browser-ui-test");
 
 // If a test fails or errors, we will retry it two more times in case it was a flaky failure.
 const NB_RETRY = 3;
@@ -200,7 +200,7 @@ async function main(argv) {
     const framework_options = new Options();
     try {
         // This is more convenient that setting fields one by one.
-        let args = [
+        const args = [
             "--variable", "DOC_PATH", opts["doc_folder"], "--enable-fail-on-js-error",
             "--allow-file-access-from-files",
         ];
@@ -234,7 +234,7 @@ async function main(argv) {
     } else {
         files = opts["files"];
     }
-    files = files.filter(file => path.extname(file) == ".goml");
+    files = files.filter(file => path.extname(file) === ".goml");
     if (files.length === 0) {
         console.error("rustdoc-gui: No test selected");
         process.exit(2);
@@ -259,7 +259,7 @@ async function main(argv) {
 
     // We catch this "event" to display a nicer message in case of unexpected exit (because of a
     // missing `--no-sandbox`).
-    const exitHandling = (code) => {
+    const exitHandling = () => {
         if (!opts["no_sandbox"]) {
             console.log("");
             console.log(
@@ -268,10 +268,10 @@ async function main(argv) {
             console.log("");
         }
     };
-    process.on('exit', exitHandling);
+    process.on("exit", exitHandling);
 
     const originalFilesLen = files.length;
-    let results = createEmptyResults();
+    const results = createEmptyResults();
     const status_bar = char_printer(files.length);
 
     let new_results;
@@ -281,7 +281,7 @@ async function main(argv) {
         Array.prototype.push.apply(results.successful, new_results.successful);
         // We generate the new list of files with the previously failing tests.
         files = Array.prototype.concat(new_results.failed, new_results.errored).map(
-            f => f['file_name']);
+            f => f["file_name"]);
         if (files.length > originalFilesLen / 2) {
             // If we have too many failing tests, it's very likely not flaky failures anymore so
             // no need to retry.
diff --git a/src/tools/rustdoc-js/.eslintrc.js b/src/tools/rustdoc-js/.eslintrc.js
new file mode 100644
index 00000000000..4ab3a315733
--- /dev/null
+++ b/src/tools/rustdoc-js/.eslintrc.js
@@ -0,0 +1,96 @@
+module.exports = {
+    "env": {
+        "browser": true,
+        "node": true,
+        "es6": true
+    },
+    "extends": "eslint:recommended",
+    "parserOptions": {
+        "ecmaVersion": 2015,
+        "sourceType": "module"
+    },
+    "rules": {
+        "linebreak-style": [
+            "error",
+            "unix"
+        ],
+        "semi": [
+            "error",
+            "always"
+        ],
+        "quotes": [
+            "error",
+            "double"
+        ],
+        "linebreak-style": [
+            "error",
+            "unix"
+        ],
+        "no-trailing-spaces": "error",
+        "no-var": ["error"],
+        "prefer-const": ["error"],
+        "prefer-arrow-callback": ["error"],
+        "brace-style": [
+            "error",
+            "1tbs",
+            { "allowSingleLine": false }
+        ],
+        "keyword-spacing": [
+            "error",
+            { "before": true, "after": true }
+        ],
+        "arrow-spacing": [
+            "error",
+            { "before": true, "after": true }
+        ],
+        "key-spacing": [
+            "error",
+            { "beforeColon": false, "afterColon": true, "mode": "strict" }
+        ],
+        "func-call-spacing": ["error", "never"],
+        "space-infix-ops": "error",
+        "space-before-function-paren": ["error", "never"],
+        "space-before-blocks": "error",
+        "comma-dangle": ["error", "always-multiline"],
+        "comma-style": ["error", "last"],
+        "max-len": ["error", { "code": 100, "tabWidth": 4 }],
+        "eol-last": ["error", "always"],
+        "arrow-parens": ["error", "as-needed"],
+        "no-unused-vars": [
+            "error",
+            {
+                "argsIgnorePattern": "^_",
+                "varsIgnorePattern": "^_"
+            }
+        ],
+        "eqeqeq": "error",
+        "no-const-assign": "error",
+        "no-debugger": "error",
+        "no-dupe-args": "error",
+        "no-dupe-else-if": "error",
+        "no-dupe-keys": "error",
+        "no-duplicate-case": "error",
+        "no-ex-assign": "error",
+        "no-fallthrough": "error",
+        "no-invalid-regexp": "error",
+        "no-import-assign": "error",
+        "no-self-compare": "error",
+        "no-template-curly-in-string": "error",
+        "block-scoped-var": "error",
+        "guard-for-in": "error",
+        "no-alert": "error",
+        "no-confusing-arrow": "error",
+        "no-div-regex": "error",
+        "no-floating-decimal": "error",
+        "no-implicit-globals": "error",
+        "no-implied-eval": "error",
+        "no-label-var": "error",
+        "no-lonely-if": "error",
+        "no-mixed-operators": "error",
+        "no-multi-assign": "error",
+        "no-return-assign": "error",
+        "no-script-url": "error",
+        "no-sequences": "error",
+        "no-div-regex": "error",
+    }
+};
diff --git a/src/tools/rustdoc-js/tester.js b/src/tools/rustdoc-js/tester.js
index e617ceba3b9..8d46a8ce7f1 100644
--- a/src/tools/rustdoc-js/tester.js
+++ b/src/tools/rustdoc-js/tester.js
@@ -1,5 +1,5 @@
-const fs = require('fs');
-const path = require('path');
+const fs = require("fs");
+const path = require("path");
 
 function loadContent(content) {
     const Module = module.constructor;
@@ -15,7 +15,7 @@ function loadContent(content) {
 }
 
 function readFile(filePath) {
-    return fs.readFileSync(filePath, 'utf8');
+    return fs.readFileSync(filePath, "utf8");
 }
 
 function contentToDiffLine(key, value) {
@@ -25,41 +25,41 @@ function contentToDiffLine(key, value) {
 // This function is only called when no matching result was found and therefore will only display
 // the diff between the two items.
 function betterLookingDiff(entry, data) {
-    let output = ' {\n';
-    const spaces = '     ';
+    let output = " {\n";
+    const spaces = "     ";
     for (const key in entry) {
-        if (!entry.hasOwnProperty(key)) {
+        if (!Object.prototype.hasOwnProperty.call(entry, key)) {
             continue;
         }
-        if (!data || !data.hasOwnProperty(key)) {
-            output += '-' + spaces + contentToDiffLine(key, entry[key]) + '\n';
+        if (!data || !Object.prototype.hasOwnProperty.call(data, key)) {
+            output += "-" + spaces + contentToDiffLine(key, entry[key]) + "\n";
             continue;
         }
         const value = data[key];
         if (value !== entry[key]) {
-            output += '-' + spaces + contentToDiffLine(key, entry[key]) + '\n';
-            output += '+' + spaces + contentToDiffLine(key, value) + '\n';
+            output += "-" + spaces + contentToDiffLine(key, entry[key]) + "\n";
+            output += "+" + spaces + contentToDiffLine(key, value) + "\n";
         } else {
-            output += spaces + contentToDiffLine(key, value) + '\n';
+            output += spaces + contentToDiffLine(key, value) + "\n";
         }
     }
-    return output + ' }';
+    return output + " }";
 }
 
 function lookForEntry(entry, data) {
     return data.findIndex(data_entry => {
         let allGood = true;
         for (const key in entry) {
-            if (!entry.hasOwnProperty(key)) {
+            if (!Object.prototype.hasOwnProperty.call(entry, key)) {
                 continue;
             }
             let value = data_entry[key];
             // To make our life easier, if there is a "parent" type, we add it to the path.
-            if (key === 'path' && data_entry['parent'] !== undefined) {
+            if (key === "path" && data_entry["parent"] !== undefined) {
                 if (value.length > 0) {
-                    value += '::' + data_entry['parent']['name'];
+                    value += "::" + data_entry["parent"]["name"];
                 } else {
-                    value = data_entry['parent']['name'];
+                    value = data_entry["parent"]["name"];
                 }
             }
             if (value !== entry[key]) {
@@ -95,7 +95,7 @@ function checkNeededFields(fullPath, expected, error_text, queryName, position)
         fieldsToCheck = [];
     }
     for (const field of fieldsToCheck) {
-        if (!expected.hasOwnProperty(field)) {
+        if (!Object.prototype.hasOwnProperty.call(expected, field)) {
             let text = `${queryName}==> Mandatory key \`${field}\` is not present`;
             if (fullPath.length > 0) {
                 text += ` in field \`${fullPath}\``;
@@ -117,22 +117,22 @@ function valueCheck(fullPath, expected, result, error_text, queryName) {
                 error_text.push(`${queryName}==> EXPECTED has extra value in array from field ` +
                     `\`${fullPath}\` (position ${i}): \`${JSON.stringify(expected[i])}\``);
             } else {
-                valueCheck(fullPath + '[' + i + ']', expected[i], result[i], error_text, queryName);
+                valueCheck(fullPath + "[" + i + "]", expected[i], result[i], error_text, queryName);
             }
         }
         for (; i < result.length; ++i) {
             error_text.push(`${queryName}==> RESULT has extra value in array from field ` +
                 `\`${fullPath}\` (position ${i}): \`${JSON.stringify(result[i])}\` ` +
-                'compared to EXPECTED');
+                "compared to EXPECTED");
         }
     } else if (expected !== null && typeof expected !== "undefined" &&
-               expected.constructor == Object) {
+               expected.constructor == Object) { // eslint-disable-line eqeqeq
         for (const key in expected) {
-            if (!expected.hasOwnProperty(key)) {
+            if (!Object.prototype.hasOwnProperty.call(expected, key)) {
                 continue;
             }
-            if (!result.hasOwnProperty(key)) {
-                error_text.push('==> Unknown key "' + key + '"');
+            if (!Object.prototype.hasOwnProperty.call(result, key)) {
+                error_text.push("==> Unknown key \"" + key + "\"");
                 break;
             }
             let result_v = result[key];
@@ -147,13 +147,13 @@ function valueCheck(fullPath, expected, result, error_text, queryName) {
                 });
                 result_v = result_v.join("");
             }
-            const obj_path = fullPath + (fullPath.length > 0 ? '.' : '') + key;
+            const obj_path = fullPath + (fullPath.length > 0 ? "." : "") + key;
             valueCheck(obj_path, expected[key], result_v, error_text, queryName);
         }
     } else {
         const expectedValue = JSON.stringify(expected);
         const resultValue = JSON.stringify(result);
-        if (expectedValue != resultValue) {
+        if (expectedValue !== resultValue) {
             error_text.push(`${queryName}==> Different values for field \`${fullPath}\`:\n` +
                 `EXPECTED: \`${expectedValue}\`\nRESULT:   \`${resultValue}\``);
         }
@@ -164,7 +164,7 @@ function runParser(query, expected, parseQuery, queryName) {
     const error_text = [];
     checkNeededFields("", expected, error_text, queryName, null);
     if (error_text.length === 0) {
-        valueCheck('', expected, parseQuery(query), error_text, queryName);
+        valueCheck("", expected, parseQuery(query), error_text, queryName);
     }
     return error_text;
 }
@@ -177,16 +177,16 @@ function runSearch(query, expected, doSearch, loadedFile, queryName) {
     const error_text = [];
 
     for (const key in expected) {
-        if (!expected.hasOwnProperty(key)) {
+        if (!Object.prototype.hasOwnProperty.call(expected, key)) {
             continue;
         }
-        if (!results.hasOwnProperty(key)) {
-            error_text.push('==> Unknown key "' + key + '"');
+        if (!Object.prototype.hasOwnProperty.call(results, key)) {
+            error_text.push("==> Unknown key \"" + key + "\"");
             break;
         }
         const entry = expected[key];
 
-        if (exact_check == true && entry.length !== results[key].length) {
+        if (exact_check && entry.length !== results[key].length) {
             error_text.push(queryName + "==> Expected exactly " + entry.length +
                             " results but found " + results[key].length + " in '" + key + "'");
         }
@@ -268,7 +268,7 @@ function runCheck(loadedFile, key, callback) {
 function runChecks(testFile, doSearch, parseQuery) {
     let checkExpected = false;
     let checkParsed = false;
-    let testFileContent = readFile(testFile) + 'exports.QUERY = QUERY;';
+    let testFileContent = readFile(testFile) + "exports.QUERY = QUERY;";
 
     if (testFileContent.indexOf("FILTER_CRATE") !== -1) {
         testFileContent += "exports.FILTER_CRATE = FILTER_CRATE;";
@@ -277,11 +277,11 @@ function runChecks(testFile, doSearch, parseQuery) {
     }
 
     if (testFileContent.indexOf("\nconst EXPECTED") !== -1) {
-        testFileContent += 'exports.EXPECTED = EXPECTED;';
+        testFileContent += "exports.EXPECTED = EXPECTED;";
         checkExpected = true;
     }
     if (testFileContent.indexOf("\nconst PARSED") !== -1) {
-        testFileContent += 'exports.PARSED = PARSED;';
+        testFileContent += "exports.PARSED = PARSED;";
         checkParsed = true;
     }
     if (!checkParsed && !checkExpected) {
@@ -325,7 +325,7 @@ function loadSearchJS(doc_folder, resource_suffix) {
     const searchWords = searchModule.initSearch(searchIndex.searchIndex);
 
     return {
-        doSearch: function (queryStr, filterCrate, currentCrate) {
+        doSearch: function(queryStr, filterCrate, currentCrate) {
             return searchModule.execQuery(searchModule.parseQuery(queryStr), searchWords,
                 filterCrate, currentCrate);
         },
@@ -361,22 +361,24 @@ function parseOptions(args) {
     };
 
     for (let i = 0; i < args.length; ++i) {
-        if (correspondences.hasOwnProperty(args[i])) {
+        const arg = args[i];
+        if (Object.prototype.hasOwnProperty.call(correspondences, arg)) {
             i += 1;
             if (i >= args.length) {
-                console.log("Missing argument after `" + args[i - 1] + "` option.");
+                console.log("Missing argument after `" + arg + "` option.");
                 return null;
             }
-            if (args[i - 1] !== "--test-file") {
-                opts[correspondences[args[i - 1]]] = args[i];
+            const arg_value = args[i];
+            if (arg !== "--test-file") {
+                opts[correspondences[arg]] = arg_value;
             } else {
-                opts[correspondences[args[i - 1]]].push(args[i]);
+                opts[correspondences[arg]].push(arg_value);
             }
-        } else if (args[i] === "--help") {
+        } else if (arg === "--help") {
             showHelp();
             process.exit(0);
         } else {
-            console.log("Unknown option `" + args[i] + "`.");
+            console.log("Unknown option `" + arg + "`.");
             console.log("Use `--help` to see the list of options");
             return null;
         }
@@ -405,17 +407,17 @@ function main(argv) {
     );
     let errors = 0;
 
-    const doSearch = function (queryStr, filterCrate) {
+    const doSearch = function(queryStr, filterCrate) {
         return parseAndSearch.doSearch(queryStr, filterCrate, opts["crate_name"]);
     };
 
     if (opts["test_file"].length !== 0) {
-        opts["test_file"].forEach(function (file) {
+        opts["test_file"].forEach(file => {
             process.stdout.write(`Testing ${file} ... `);
             errors += runChecks(file, doSearch, parseAndSearch.parseQuery);
         });
     } else if (opts["test_folder"].length !== 0) {
-        fs.readdirSync(opts["test_folder"]).forEach(function (file) {
+        fs.readdirSync(opts["test_folder"]).forEach(file => {
             if (!file.endsWith(".js")) {
                 return;
             }
diff --git a/src/tools/rustfmt/src/closures.rs b/src/tools/rustfmt/src/closures.rs
index 340113866c4..c95e9a97b43 100644
--- a/src/tools/rustfmt/src/closures.rs
+++ b/src/tools/rustfmt/src/closures.rs
@@ -195,7 +195,6 @@ fn rewrite_closure_expr(
             | ast::ExprKind::Struct(..) => true,
 
             ast::ExprKind::AddrOf(_, _, ref expr)
-            | ast::ExprKind::Box(ref expr)
             | ast::ExprKind::Try(ref expr)
             | ast::ExprKind::Unary(_, ref expr)
             | ast::ExprKind::Cast(ref expr, _) => allow_multi_line(expr),
@@ -441,7 +440,6 @@ fn is_block_closure_forced_inner(expr: &ast::Expr, version: Version) -> bool {
         ast::ExprKind::If(..) | ast::ExprKind::While(..) | ast::ExprKind::ForLoop(..) => true,
         ast::ExprKind::Loop(..) if version == Version::Two => true,
         ast::ExprKind::AddrOf(_, _, ref expr)
-        | ast::ExprKind::Box(ref expr)
         | ast::ExprKind::Try(ref expr)
         | ast::ExprKind::Unary(_, ref expr)
         | ast::ExprKind::Cast(ref expr, _) => is_block_closure_forced_inner(expr, version),
diff --git a/src/tools/rustfmt/src/expr.rs b/src/tools/rustfmt/src/expr.rs
index 3f0f217f890..7273402ec76 100644
--- a/src/tools/rustfmt/src/expr.rs
+++ b/src/tools/rustfmt/src/expr.rs
@@ -236,7 +236,6 @@ pub(crate) fn format_expr(
         ast::ExprKind::Yeet(Some(ref expr)) => {
             rewrite_unary_prefix(context, "do yeet ", &**expr, shape)
         }
-        ast::ExprKind::Box(ref expr) => rewrite_unary_prefix(context, "box ", &**expr, shape),
         ast::ExprKind::AddrOf(borrow_kind, mutability, ref expr) => {
             rewrite_expr_addrof(context, borrow_kind, mutability, expr, shape)
         }
@@ -1299,7 +1298,6 @@ pub(crate) fn is_simple_expr(expr: &ast::Expr) -> bool {
         ast::ExprKind::Lit(..) => true,
         ast::ExprKind::Path(ref qself, ref path) => qself.is_none() && path.segments.len() <= 1,
         ast::ExprKind::AddrOf(_, _, ref expr)
-        | ast::ExprKind::Box(ref expr)
         | ast::ExprKind::Cast(ref expr, _)
         | ast::ExprKind::Field(ref expr, _)
         | ast::ExprKind::Try(ref expr)
@@ -1361,7 +1359,6 @@ pub(crate) fn can_be_overflowed_expr(
 
         // Handle unary-like expressions
         ast::ExprKind::AddrOf(_, _, ref expr)
-        | ast::ExprKind::Box(ref expr)
         | ast::ExprKind::Try(ref expr)
         | ast::ExprKind::Unary(_, ref expr)
         | ast::ExprKind::Cast(ref expr, _) => can_be_overflowed_expr(context, expr, args_len),
@@ -1373,7 +1370,6 @@ pub(crate) fn is_nested_call(expr: &ast::Expr) -> bool {
     match expr.kind {
         ast::ExprKind::Call(..) | ast::ExprKind::MacCall(..) => true,
         ast::ExprKind::AddrOf(_, _, ref expr)
-        | ast::ExprKind::Box(ref expr)
         | ast::ExprKind::Try(ref expr)
         | ast::ExprKind::Unary(_, ref expr)
         | ast::ExprKind::Cast(ref expr, _) => is_nested_call(expr),
@@ -2133,7 +2129,6 @@ pub(crate) fn is_method_call(expr: &ast::Expr) -> bool {
     match expr.kind {
         ast::ExprKind::MethodCall(..) => true,
         ast::ExprKind::AddrOf(_, _, ref expr)
-        | ast::ExprKind::Box(ref expr)
         | ast::ExprKind::Cast(ref expr, _)
         | ast::ExprKind::Try(ref expr)
         | ast::ExprKind::Unary(_, ref expr) => is_method_call(expr),
diff --git a/src/tools/rustfmt/src/matches.rs b/src/tools/rustfmt/src/matches.rs
index 85d9c5d2b9b..aac5e59b860 100644
--- a/src/tools/rustfmt/src/matches.rs
+++ b/src/tools/rustfmt/src/matches.rs
@@ -592,7 +592,6 @@ fn can_flatten_block_around_this(body: &ast::Expr) -> bool {
         | ast::ExprKind::Struct(..)
         | ast::ExprKind::Tup(..) => true,
         ast::ExprKind::AddrOf(_, _, ref expr)
-        | ast::ExprKind::Box(ref expr)
         | ast::ExprKind::Try(ref expr)
         | ast::ExprKind::Unary(_, ref expr)
         | ast::ExprKind::Index(ref expr, _)
diff --git a/src/tools/rustfmt/src/utils.rs b/src/tools/rustfmt/src/utils.rs
index 1e89f3ae75f..a26375ee643 100644
--- a/src/tools/rustfmt/src/utils.rs
+++ b/src/tools/rustfmt/src/utils.rs
@@ -492,7 +492,6 @@ pub(crate) fn is_block_expr(context: &RewriteContext<'_>, expr: &ast::Expr, repr
         | ast::ExprKind::Assign(..)
         | ast::ExprKind::AssignOp(..)
         | ast::ExprKind::Await(..)
-        | ast::ExprKind::Box(..)
         | ast::ExprKind::Break(..)
         | ast::ExprKind::Cast(..)
         | ast::ExprKind::Continue(..)
diff --git a/src/tools/rustfmt/tests/source/expr.rs b/src/tools/rustfmt/tests/source/expr.rs
index 21f8a4a4366..879c551ea49 100644
--- a/src/tools/rustfmt/tests/source/expr.rs
+++ b/src/tools/rustfmt/tests/source/expr.rs
@@ -3,7 +3,6 @@
 // Test expressions
 
 fn foo() -> bool {
-    let boxed: Box<i32> = box   5;
     let referenced = &5 ;
 
     let very_long_variable_name = ( a +  first +   simple + test   );
@@ -132,12 +131,6 @@ fn qux() {
     }
 }
 
-fn issue227() {
-    {
-        let handler = box DocumentProgressHandler::new(addr, DocumentProgressTask::DOMContentLoaded);
-    }
-}
-
 fn issue184(source: &str) {
     for c in source.chars() {
         if index < 'a' {
@@ -413,10 +406,6 @@ fn issue2704() {
                                 .concat(&requires1)
                                 .concat(&requires2)
                                 .distinct_total());
-    let requires = requires.set(box requires0
-                                .concat(&requires1)
-                                .concat(&requires2)
-                                .distinct_total());
     let requires = requires.set(requires0
                                 .concat(&requires1)
                                 .concat(&requires2)
diff --git a/src/tools/rustfmt/tests/target/configs/combine_control_expr/false.rs b/src/tools/rustfmt/tests/target/configs/combine_control_expr/false.rs
index 5ada9b1dd14..0ab82024937 100644
--- a/src/tools/rustfmt/tests/target/configs/combine_control_expr/false.rs
+++ b/src/tools/rustfmt/tests/target/configs/combine_control_expr/false.rs
@@ -108,12 +108,6 @@ fn main() {
         bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb,
     ));
 
-    // Box
-    foo(box Bar {
-        aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,
-        bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb,
-    });
-
     // Unary
     foo(!bar(
         aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,
diff --git a/src/tools/rustfmt/tests/target/configs/combine_control_expr/true.rs b/src/tools/rustfmt/tests/target/configs/combine_control_expr/true.rs
index 52acd26492a..aa41e021fb7 100644
--- a/src/tools/rustfmt/tests/target/configs/combine_control_expr/true.rs
+++ b/src/tools/rustfmt/tests/target/configs/combine_control_expr/true.rs
@@ -96,12 +96,6 @@ fn main() {
         bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb,
     ));
 
-    // Box
-    foo(box Bar {
-        aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,
-        bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb,
-    });
-
     // Unary
     foo(!bar(
         aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,
diff --git a/src/tools/rustfmt/tests/target/expr.rs b/src/tools/rustfmt/tests/target/expr.rs
index 84df802bc70..187a1dc976a 100644
--- a/src/tools/rustfmt/tests/target/expr.rs
+++ b/src/tools/rustfmt/tests/target/expr.rs
@@ -3,7 +3,6 @@
 // Test expressions
 
 fn foo() -> bool {
-    let boxed: Box<i32> = box 5;
     let referenced = &5;
 
     let very_long_variable_name = (a + first + simple + test);
@@ -179,13 +178,6 @@ fn qux() {
     }
 }
 
-fn issue227() {
-    {
-        let handler =
-            box DocumentProgressHandler::new(addr, DocumentProgressTask::DOMContentLoaded);
-    }
-}
-
 fn issue184(source: &str) {
     for c in source.chars() {
         if index < 'a' {
@@ -455,12 +447,6 @@ fn issue2704() {
             .distinct_total(),
     );
     let requires = requires.set(
-        box requires0
-            .concat(&requires1)
-            .concat(&requires2)
-            .distinct_total(),
-    );
-    let requires = requires.set(
         requires0
             .concat(&requires1)
             .concat(&requires2)
diff --git a/src/tools/tidy/src/pal.rs b/src/tools/tidy/src/pal.rs
index 6d6d3c89a3c..868579b4b1a 100644
--- a/src/tools/tidy/src/pal.rs
+++ b/src/tools/tidy/src/pal.rs
@@ -62,8 +62,6 @@ const EXCEPTION_PATHS: &[&str] = &[
     "library/std/src/panic.rs",   // fuchsia-specific panic backtrace handling
     "library/std/src/personality.rs",
     "library/std/src/personality/",
-    "library/std/src/thread/mod.rs",
-    "library/std/src/thread/local.rs",
 ];
 
 pub fn check(path: &Path, bad: &mut bool) {
diff --git a/src/tools/tidy/src/style.rs b/src/tools/tidy/src/style.rs
index 9ecb30529cc..75a4586cb7f 100644
--- a/src/tools/tidy/src/style.rs
+++ b/src/tools/tidy/src/style.rs
@@ -171,9 +171,9 @@ fn contains_ignore_directive(can_contain: bool, contents: &str, check: &str) ->
 }
 
 macro_rules! suppressible_tidy_err {
-    ($err:ident, $skip:ident, $msg:expr) => {
+    ($err:ident, $skip:ident, $msg:literal) => {
         if let Directive::Deny = $skip {
-            $err($msg);
+            $err(&format!($msg));
         } else {
             $skip = Directive::Ignore(true);
         }
@@ -300,10 +300,13 @@ pub fn check(path: &Path, bad: &mut bool) {
             contains_ignore_directive(can_contain, &contents, "leading-newlines");
         let mut skip_copyright = contains_ignore_directive(can_contain, &contents, "copyright");
         let mut skip_dbg = contains_ignore_directive(can_contain, &contents, "dbg");
+        let mut skip_odd_backticks =
+            contains_ignore_directive(can_contain, &contents, "odd-backticks");
         let mut leading_new_lines = false;
         let mut trailing_new_lines = 0;
         let mut lines = 0;
         let mut last_safety_comment = false;
+        let mut comment_block: Option<(usize, usize)> = None;
         let is_test = file.components().any(|c| c.as_os_str() == "tests");
         // scanning the whole file for multiple needles at once is more efficient than
         // executing lines times needles separate searches.
@@ -351,7 +354,7 @@ pub fn check(path: &Path, bad: &mut bool) {
                 suppressible_tidy_err!(
                     err,
                     skip_line_length,
-                    &format!("line longer than {max_columns} chars")
+                    "line longer than {max_columns} chars"
                 );
             }
             if !is_style_file && line.contains('\t') {
@@ -415,15 +418,55 @@ pub fn check(path: &Path, bad: &mut bool) {
 
             // For now only enforce in compiler
             let is_compiler = || file.components().any(|c| c.as_os_str() == "compiler");
-            if is_compiler()
-                && line.contains("//")
-                && line
-                    .chars()
-                    .collect::<Vec<_>>()
-                    .windows(4)
-                    .any(|cs| matches!(cs, ['.', ' ', ' ', last] if last.is_alphabetic()))
-            {
-                err(DOUBLE_SPACE_AFTER_DOT)
+
+            if is_compiler() {
+                if line.contains("//")
+                    && line
+                        .chars()
+                        .collect::<Vec<_>>()
+                        .windows(4)
+                        .any(|cs| matches!(cs, ['.', ' ', ' ', last] if last.is_alphabetic()))
+                {
+                    err(DOUBLE_SPACE_AFTER_DOT)
+                }
+
+                if filename.ends_with(".ftl") {
+                    let line_backticks = trimmed.chars().filter(|ch| *ch == '`').count();
+                    if line_backticks % 2 == 1 {
+                        suppressible_tidy_err!(err, skip_odd_backticks, "odd number of backticks");
+                    }
+                } else if trimmed.contains("//") {
+                    let (start_line, mut backtick_count) = comment_block.unwrap_or((i + 1, 0));
+                    let line_backticks = trimmed.chars().filter(|ch| *ch == '`').count();
+                    let comment_text = trimmed.split("//").nth(1).unwrap();
+                    // This check ensures that we don't lint for code that has `//` in a string literal
+                    if line_backticks % 2 == 1 {
+                        backtick_count += comment_text.chars().filter(|ch| *ch == '`').count();
+                    }
+                    comment_block = Some((start_line, backtick_count));
+                } else {
+                    if let Some((start_line, backtick_count)) = comment_block.take() {
+                        if backtick_count % 2 == 1 {
+                            let mut err = |msg: &str| {
+                                tidy_error!(bad, "{}:{start_line}: {msg}", file.display());
+                            };
+                            let block_len = (i + 1) - start_line;
+                            if block_len == 1 {
+                                suppressible_tidy_err!(
+                                    err,
+                                    skip_odd_backticks,
+                                    "comment with odd number of backticks"
+                                );
+                            } else {
+                                suppressible_tidy_err!(
+                                    err,
+                                    skip_odd_backticks,
+                                    "{block_len}-line comment block with odd number of backticks"
+                                );
+                            }
+                        }
+                    }
+                }
             }
         }
         if leading_new_lines {
@@ -441,7 +484,7 @@ pub fn check(path: &Path, bad: &mut bool) {
             n => suppressible_tidy_err!(
                 err,
                 skip_trailing_newlines,
-                &format!("too many trailing newlines ({n})")
+                "too many trailing newlines ({n})"
             ),
         };
         if lines > LINES {