about summary refs log tree commit diff
path: root/src/tools
diff options
context:
space:
mode:
authorRalf Jung <post@ralfj.de>2025-06-14 05:29:34 +0000
committerGitHub <noreply@github.com>2025-06-14 05:29:34 +0000
commit974779746f36c705c351945c8a5d8c48eec0681e (patch)
tree32cc30d8e7b03b639193725a28787a0733b4330b /src/tools
parentf5e10748e53b543131b6472af3b3a2b6ff866e9b (diff)
parente5b0c02c42e6399b1aaeafaa949615dbbae35417 (diff)
downloadrust-974779746f36c705c351945c8a5d8c48eec0681e.tar.gz
rust-974779746f36c705c351945c8a5d8c48eec0681e.zip
Merge pull request #4398 from rust-lang/rustup-2025-06-14
Automatic Rustup
Diffstat (limited to 'src/tools')
m---------src/tools/cargo0
-rw-r--r--src/tools/clippy/CHANGELOG.md5
-rw-r--r--src/tools/clippy/clippy_dev/src/update_lints.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/attrs/deprecated_cfg_attr.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/attrs/duplicated_attributes.rs18
-rw-r--r--src/tools/clippy/clippy_lints/src/booleans.rs16
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs19
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs14
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/cast_possible_wrap.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/cast_precision_loss.rs16
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast.rs14
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast_with_truncation.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/utils.rs29
-rw-r--r--src/tools/clippy/clippy_lints/src/checked_conversions.rs60
-rw-r--r--src/tools/clippy/clippy_lints/src/cloned_ref_to_slice_refs.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/coerce_container_to_any.rs108
-rw-r--r--src/tools/clippy/clippy_lints/src/copies.rs51
-rw-r--r--src/tools/clippy/clippy_lints/src/create_dir.rs23
-rw-r--r--src/tools/clippy/clippy_lints/src/ctfe.rs26
-rw-r--r--src/tools/clippy/clippy_lints/src/declared_lints.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/default.rs18
-rw-r--r--src/tools/clippy/clippy_lints/src/disallowed_names.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/disallowed_types.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/doc/doc_suspicious_footnotes.rs113
-rw-r--r--src/tools/clippy/clippy_lints/src/doc/mod.rs40
-rw-r--r--src/tools/clippy/clippy_lints/src/endian_bytes.rs12
-rw-r--r--src/tools/clippy/clippy_lints/src/format_args.rs124
-rw-r--r--src/tools/clippy/clippy_lints/src/functions/mod.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/if_let_mutex.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs12
-rw-r--r--src/tools/clippy/clippy_lints/src/infallible_try_from.rs76
-rw-r--r--src/tools/clippy/clippy_lints/src/lib.rs12
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs38
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_clamp.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_string_new.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/match_result_ok.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/match_single_binding.rs18
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/match_str_case_mismatch.rs17
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/ip_constant.rs52
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/manual_saturating_arithmetic.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/mod.rs39
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/needless_collect.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/open_options.rs18
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/str_splitn.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/multiple_bound_locations.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/non_canonical_impls.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/pathbuf_init_then_push.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/ptr.rs26
-rw-r--r--src/tools/clippy/clippy_lints/src/read_zero_byte_vec.rs12
-rw-r--r--src/tools/clippy/clippy_lints/src/reserve_after_initialization.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/semicolon_block.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/serde_api.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/std_instead_of_core.rs79
-rw-r--r--src/tools/clippy/clippy_lints/src/strings.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/swap.rs17
-rw-r--r--src/tools/clippy/clippy_lints/src/transmute/missing_transmute_annotations.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/types/borrowed_box.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs124
-rw-r--r--src/tools/clippy/clippy_lints/src/unused_io_amount.rs16
-rw-r--r--src/tools/clippy/clippy_lints/src/unused_result_ok.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/unused_unit.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/vec_init_then_push.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/wildcard_imports.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/write.rs64
-rw-r--r--src/tools/clippy/clippy_lints/src/zombie_processes.rs26
-rw-r--r--src/tools/clippy/clippy_lints_internal/src/almost_standard_lint_formulation.rs2
-rw-r--r--src/tools/clippy/clippy_lints_internal/src/lint_without_lint_pass.rs2
-rw-r--r--src/tools/clippy/clippy_utils/README.md2
-rw-r--r--src/tools/clippy/clippy_utils/src/lib.rs15
-rw-r--r--src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs12
-rw-r--r--src/tools/clippy/clippy_utils/src/sym.rs22
-rw-r--r--src/tools/clippy/clippy_utils/src/ty/mod.rs37
-rw-r--r--src/tools/clippy/rust-toolchain.toml2
-rw-r--r--src/tools/clippy/tests/compile-test.rs8
-rw-r--r--src/tools/clippy/tests/symbols-used.rs81
-rw-r--r--src/tools/clippy/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs1
-rw-r--r--src/tools/clippy/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr6
-rw-r--r--src/tools/clippy/tests/ui/branches_sharing_code/shared_at_bottom.rs37
-rw-r--r--src/tools/clippy/tests/ui/branches_sharing_code/shared_at_bottom.stderr17
-rw-r--r--src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top.rs31
-rw-r--r--src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top.stderr17
-rw-r--r--src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs39
-rw-r--r--src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top_and_bottom.stderr28
-rw-r--r--src/tools/clippy/tests/ui/cmp_null.fixed6
-rw-r--r--src/tools/clippy/tests/ui/cmp_null.rs6
-rw-r--r--src/tools/clippy/tests/ui/cmp_null.stderr8
-rw-r--r--src/tools/clippy/tests/ui/coerce_container_to_any.fixed26
-rw-r--r--src/tools/clippy/tests/ui/coerce_container_to_any.rs26
-rw-r--r--src/tools/clippy/tests/ui/coerce_container_to_any.stderr23
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-14935.rs27
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-9463.rs7
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-9463.stderr29
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-rust-107877.rs1
-rw-r--r--src/tools/clippy/tests/ui/create_dir.fixed23
-rw-r--r--src/tools/clippy/tests/ui/create_dir.rs19
-rw-r--r--src/tools/clippy/tests/ui/create_dir.stderr43
-rw-r--r--src/tools/clippy/tests/ui/disallowed_names.rs15
-rw-r--r--src/tools/clippy/tests/ui/disallowed_names.stderr28
-rw-r--r--src/tools/clippy/tests/ui/doc_suspicious_footnotes.fixed186
-rw-r--r--src/tools/clippy/tests/ui/doc_suspicious_footnotes.rs162
-rw-r--r--src/tools/clippy/tests/ui/doc_suspicious_footnotes.stderr179
-rw-r--r--src/tools/clippy/tests/ui/doc_suspicious_footnotes_include.rs4
-rw-r--r--src/tools/clippy/tests/ui/doc_suspicious_footnotes_include.stderr17
-rw-r--r--src/tools/clippy/tests/ui/doc_suspicious_footnotes_include.txt13
-rw-r--r--src/tools/clippy/tests/ui/format_args.fixed10
-rw-r--r--src/tools/clippy/tests/ui/format_args.rs10
-rw-r--r--src/tools/clippy/tests/ui/indexing_slicing_index.rs1
-rw-r--r--src/tools/clippy/tests/ui/indexing_slicing_index.stderr31
-rw-r--r--src/tools/clippy/tests/ui/infallible_try_from.rs33
-rw-r--r--src/tools/clippy/tests/ui/infallible_try_from.stderr23
-rw-r--r--src/tools/clippy/tests/ui/ip_constant.fixed107
-rw-r--r--src/tools/clippy/tests/ui/ip_constant.rs127
-rw-r--r--src/tools/clippy/tests/ui/ip_constant.stderr338
-rw-r--r--src/tools/clippy/tests/ui/ip_constant_from_external.rs12
-rw-r--r--src/tools/clippy/tests/ui/ip_constant_from_external.stderr16
-rw-r--r--src/tools/clippy/tests/ui/localhost.txt1
-rw-r--r--src/tools/clippy/tests/ui/manual_flatten.fixed148
-rw-r--r--src/tools/clippy/tests/ui/manual_flatten.rs39
-rw-r--r--src/tools/clippy/tests/ui/manual_flatten.stderr139
-rw-r--r--src/tools/clippy/tests/ui/manual_swap_auto_fix.fixed11
-rw-r--r--src/tools/clippy/tests/ui/manual_swap_auto_fix.rs14
-rw-r--r--src/tools/clippy/tests/ui/manual_swap_auto_fix.stderr11
-rw-r--r--src/tools/clippy/tests/ui/match_single_binding.fixed16
-rw-r--r--src/tools/clippy/tests/ui/match_single_binding.rs18
-rw-r--r--src/tools/clippy/tests/ui/match_single_binding.stderr35
-rw-r--r--src/tools/clippy/tests/ui/missing_const_for_fn/const_trait.fixed36
-rw-r--r--src/tools/clippy/tests/ui/missing_const_for_fn/const_trait.rs36
-rw-r--r--src/tools/clippy/tests/ui/missing_const_for_fn/const_trait.stderr17
-rw-r--r--src/tools/clippy/tests/ui/needless_lifetimes.fixed2
-rw-r--r--src/tools/clippy/tests/ui/needless_lifetimes.rs2
-rw-r--r--src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.fixed16
-rw-r--r--src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.rs18
-rw-r--r--src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.stderr15
-rw-r--r--src/tools/clippy/tests/ui/pointer_format.rs66
-rw-r--r--src/tools/clippy/tests/ui/pointer_format.stderr47
-rw-r--r--src/tools/clippy/tests/ui/print_literal.fixed11
-rw-r--r--src/tools/clippy/tests/ui/print_literal.rs11
-rw-r--r--src/tools/clippy/tests/ui/print_literal.stderr50
-rw-r--r--src/tools/clippy/tests/ui/semicolon_outside_block.fixed25
-rw-r--r--src/tools/clippy/tests/ui/semicolon_outside_block.rs25
-rw-r--r--src/tools/clippy/tests/ui/std_instead_of_core.fixed6
-rw-r--r--src/tools/clippy/tests/ui/std_instead_of_core.rs6
-rw-r--r--src/tools/clippy/tests/ui/unit_arg.rs24
-rw-r--r--src/tools/clippy/tests/ui/unit_arg.stderr23
-rw-r--r--src/tools/clippy/tests/ui/unit_arg_empty_blocks.fixed34
-rw-r--r--src/tools/clippy/tests/ui/unit_arg_empty_blocks.rs31
-rw-r--r--src/tools/clippy/tests/ui/unit_arg_empty_blocks.stderr46
-rw-r--r--src/tools/clippy/tests/ui/unit_arg_fixable.fixed78
-rw-r--r--src/tools/clippy/tests/ui/unit_arg_fixable.rs71
-rw-r--r--src/tools/clippy/tests/ui/unit_arg_fixable.stderr110
-rw-r--r--src/tools/clippy/tests/ui/unused_unit.edition2021.fixed8
-rw-r--r--src/tools/clippy/tests/ui/unused_unit.edition2024.fixed8
-rw-r--r--src/tools/clippy/tests/ui/unused_unit.rs8
-rw-r--r--src/tools/clippy/tests/ui/write_literal.fixed12
-rw-r--r--src/tools/clippy/tests/ui/write_literal.rs12
-rw-r--r--src/tools/clippy/tests/ui/write_literal.stderr50
-rw-r--r--src/tools/clippy/tests/ui/zombie_processes.rs10
-rw-r--r--src/tools/clippy/util/gh-pages/script.js2
-rw-r--r--src/tools/compiletest/src/directive-list.rs1
-rw-r--r--src/tools/compiletest/src/header/needs.rs5
-rw-r--r--src/tools/compiletest/src/header/tests.rs11
-rw-r--r--src/tools/miri/README.md5
-rw-r--r--src/tools/miri/rust-version2
-rw-r--r--src/tools/miri/src/bin/miri.rs2
-rw-r--r--src/tools/miri/src/eval.rs3
-rw-r--r--src/tools/miri/src/intrinsics/mod.rs24
-rw-r--r--src/tools/miri/src/intrinsics/simd.rs7
-rw-r--r--src/tools/miri/src/machine.rs5
-rw-r--r--src/tools/miri/src/math.rs4
-rw-r--r--src/tools/miri/src/operator.rs9
-rw-r--r--src/tools/rust-analyzer/.github/workflows/autopublish.yaml6
-rw-r--r--src/tools/rust-analyzer/.github/workflows/publish-libs.yaml2
-rw-r--r--src/tools/rust-analyzer/Cargo.toml2
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/dyn_map.rs10
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs11
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/db.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer.rs12
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/lower.rs20
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs253
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs48
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/diagnostics.rs3
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/semantics.rs379
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs28
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs37
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs102
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs102
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs147
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_underscore.rs3
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs77
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/diagnostic.rs60
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs78
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/rename.rs200
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs1
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs157
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs1
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/expand_macro.rs61
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs64
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/bounds.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs15
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/extern_block.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs5
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_static.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/implied_dyn_trait.rs133
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/lifetime.rs10
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/range_exclusive.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/rename.rs238
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs4
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/grammar/generic_params.rs11
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/grammar/paths.rs7
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/grammar/types.rs14
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs2
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs8
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/err/0024_many_type_parens.rast15
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/angled_path_without_qual.rast42
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/type_bounds_macro_call_recovery.rast112
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/type_bounds_macro_call_recovery.rs1
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/call_expr.rast46
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/closure_binder.rast36
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/closure_binder.rs1
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/lambda_expr.rast30
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/qual_paths.rast62
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/type_path_in_pattern.rast9
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/where_clause.rast31
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0036_fully_qualified.rast31
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0042_ufcs_call_list.rast15
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0067_where_for_pred.rast31
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl.rs133
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs137
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs168
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs6
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs14
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs4
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/utils.rs3
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs4
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs10
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/ratoml.rs1
-rw-r--r--src/tools/rust-analyzer/crates/syntax/rust.ungram7
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs66
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs23
-rw-r--r--src/tools/rust-analyzer/crates/test-fixture/src/lib.rs69
-rw-r--r--src/tools/rust-analyzer/docs/book/src/non_cargo_based_projects.md2
-rw-r--r--src/tools/rust-analyzer/editors/code/package-lock.json6
-rw-r--r--src/tools/rust-analyzer/rust-version2
-rw-r--r--src/tools/rustbook/Cargo.lock180
-rw-r--r--src/tools/tidy/src/deps.rs1
254 files changed, 6634 insertions, 1861 deletions
diff --git a/src/tools/cargo b/src/tools/cargo
-Subproject 64a12460708cf146e16cc61f28aba5dc2463bbb
+Subproject fc1518ef02b77327d70d4026b95ea719dd9b8c5
diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md
index 3a98217f625..0cfe89ad378 100644
--- a/src/tools/clippy/CHANGELOG.md
+++ b/src/tools/clippy/CHANGELOG.md
@@ -5685,6 +5685,7 @@ Released 2018-09-13
 [`cmp_nan`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_nan
 [`cmp_null`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_null
 [`cmp_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_owned
+[`coerce_container_to_any`]: https://rust-lang.github.io/rust-clippy/master/index.html#coerce_container_to_any
 [`cognitive_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity
 [`collapsible_else_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_else_if
 [`collapsible_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if
@@ -5736,6 +5737,7 @@ Released 2018-09-13
 [`doc_markdown`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown
 [`doc_nested_refdefs`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_nested_refdefs
 [`doc_overindented_list_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_overindented_list_items
+[`doc_suspicious_footnotes`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_suspicious_footnotes
 [`double_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_comparisons
 [`double_ended_iterator_last`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_ended_iterator_last
 [`double_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_must_use
@@ -5862,6 +5864,7 @@ Released 2018-09-13
 [`ineffective_open_options`]: https://rust-lang.github.io/rust-clippy/master/index.html#ineffective_open_options
 [`inefficient_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#inefficient_to_string
 [`infallible_destructuring_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#infallible_destructuring_match
+[`infallible_try_from`]: https://rust-lang.github.io/rust-clippy/master/index.html#infallible_try_from
 [`infinite_iter`]: https://rust-lang.github.io/rust-clippy/master/index.html#infinite_iter
 [`infinite_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#infinite_loop
 [`inherent_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#inherent_to_string
@@ -5888,6 +5891,7 @@ Released 2018-09-13
 [`inverted_saturating_sub`]: https://rust-lang.github.io/rust-clippy/master/index.html#inverted_saturating_sub
 [`invisible_characters`]: https://rust-lang.github.io/rust-clippy/master/index.html#invisible_characters
 [`io_other_error`]: https://rust-lang.github.io/rust-clippy/master/index.html#io_other_error
+[`ip_constant`]: https://rust-lang.github.io/rust-clippy/master/index.html#ip_constant
 [`is_digit_ascii_radix`]: https://rust-lang.github.io/rust-clippy/master/index.html#is_digit_ascii_radix
 [`items_after_statements`]: https://rust-lang.github.io/rust-clippy/master/index.html#items_after_statements
 [`items_after_test_module`]: https://rust-lang.github.io/rust-clippy/master/index.html#items_after_test_module
@@ -6163,6 +6167,7 @@ Released 2018-09-13
 [`pathbuf_init_then_push`]: https://rust-lang.github.io/rust-clippy/master/index.html#pathbuf_init_then_push
 [`pattern_type_mismatch`]: https://rust-lang.github.io/rust-clippy/master/index.html#pattern_type_mismatch
 [`permissions_set_readonly_false`]: https://rust-lang.github.io/rust-clippy/master/index.html#permissions_set_readonly_false
+[`pointer_format`]: https://rust-lang.github.io/rust-clippy/master/index.html#pointer_format
 [`pointers_in_nomem_asm_block`]: https://rust-lang.github.io/rust-clippy/master/index.html#pointers_in_nomem_asm_block
 [`positional_named_format_parameters`]: https://rust-lang.github.io/rust-clippy/master/index.html#positional_named_format_parameters
 [`possible_missing_comma`]: https://rust-lang.github.io/rust-clippy/master/index.html#possible_missing_comma
diff --git a/src/tools/clippy/clippy_dev/src/update_lints.rs b/src/tools/clippy/clippy_dev/src/update_lints.rs
index 320462a2c96..08592f2521f 100644
--- a/src/tools/clippy/clippy_dev/src/update_lints.rs
+++ b/src/tools/clippy/clippy_dev/src/update_lints.rs
@@ -73,8 +73,8 @@ pub fn generate_lint_files(
             (
                 "clippy_lints/src/lib.rs",
                 &mut update_text_region_fn(
-                    "// begin lints modules, do not remove this comment, it’s used in `update_lints`\n",
-                    "// end lints modules, do not remove this comment, it’s used in `update_lints`",
+                    "// begin lints modules, do not remove this comment, it's used in `update_lints`\n",
+                    "// end lints modules, do not remove this comment, it's used in `update_lints`",
                     |dst| {
                         for lint_mod in lints.iter().map(|l| &l.module).sorted().dedup() {
                             writeln!(dst, "mod {lint_mod};").unwrap();
diff --git a/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs b/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs
index 59a0c7c8868..b9ae9afe851 100644
--- a/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs
+++ b/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs
@@ -204,7 +204,7 @@ impl ArbitrarySourceItemOrdering {
                 self.assoc_types_order
             ),
             Some(before_item.span),
-            format!("should be placed before `{}`", before_item.ident.as_str(),),
+            format!("should be placed before `{}`", before_item.ident.name),
         );
     }
 
@@ -216,7 +216,7 @@ impl ArbitrarySourceItemOrdering {
             ident.span,
             "incorrect ordering of items (must be alphabetically ordered)",
             Some(before_ident.span),
-            format!("should be placed before `{}`", before_ident.as_str(),),
+            format!("should be placed before `{}`", before_ident.name),
         );
     }
 
@@ -228,7 +228,7 @@ impl ArbitrarySourceItemOrdering {
         };
 
         let (before_span, note) = if let Some(ident) = before_item.kind.ident() {
-            (ident.span, format!("should be placed before `{}`", ident.as_str(),))
+            (ident.span, format!("should be placed before `{}`", ident.name))
         } else {
             (
                 before_item.span,
@@ -255,7 +255,7 @@ impl ArbitrarySourceItemOrdering {
                 self.assoc_types_order
             ),
             Some(before_item.span),
-            format!("should be placed before `{}`", before_item.ident.as_str(),),
+            format!("should be placed before `{}`", before_item.ident.name),
         );
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/attrs/deprecated_cfg_attr.rs b/src/tools/clippy/clippy_lints/src/attrs/deprecated_cfg_attr.rs
index 0edb50be8c7..d67a194b020 100644
--- a/src/tools/clippy/clippy_lints/src/attrs/deprecated_cfg_attr.rs
+++ b/src/tools/clippy/clippy_lints/src/attrs/deprecated_cfg_attr.rs
@@ -61,7 +61,7 @@ pub(super) fn check_clippy(cx: &EarlyContext<'_>, attr: &Attribute) {
 
 fn check_deprecated_cfg_recursively(cx: &EarlyContext<'_>, attr: &rustc_ast::MetaItem) {
     if let Some(ident) = attr.ident() {
-        if ["any", "all", "not"].contains(&ident.name.as_str()) {
+        if matches!(ident.name, sym::any | sym::all | sym::not) {
             let Some(list) = attr.meta_item_list() else { return };
             for item in list.iter().filter_map(|item| item.meta_item()) {
                 check_deprecated_cfg_recursively(cx, item);
diff --git a/src/tools/clippy/clippy_lints/src/attrs/duplicated_attributes.rs b/src/tools/clippy/clippy_lints/src/attrs/duplicated_attributes.rs
index a851daaede7..c2406bcfb64 100644
--- a/src/tools/clippy/clippy_lints/src/attrs/duplicated_attributes.rs
+++ b/src/tools/clippy/clippy_lints/src/attrs/duplicated_attributes.rs
@@ -1,9 +1,10 @@
 use super::DUPLICATED_ATTRIBUTES;
 use clippy_utils::diagnostics::span_lint_and_then;
+use itertools::Itertools;
 use rustc_ast::{Attribute, MetaItem};
 use rustc_data_structures::fx::FxHashMap;
 use rustc_lint::EarlyContext;
-use rustc_span::{Span, sym};
+use rustc_span::{Span, Symbol, sym};
 use std::collections::hash_map::Entry;
 
 fn emit_if_duplicated(
@@ -29,7 +30,7 @@ fn check_duplicated_attr(
     cx: &EarlyContext<'_>,
     attr: &MetaItem,
     attr_paths: &mut FxHashMap<String, Span>,
-    parent: &mut Vec<String>,
+    parent: &mut Vec<Symbol>,
 ) {
     if attr.span.from_expansion() {
         return;
@@ -43,7 +44,7 @@ fn check_duplicated_attr(
         return;
     }
     if let Some(direct_parent) = parent.last()
-        && direct_parent == sym::cfg_trace.as_str()
+        && *direct_parent == sym::cfg_trace
         && [sym::all, sym::not, sym::any].contains(&name)
     {
         // FIXME: We don't correctly check `cfg`s for now, so if it's more complex than just a one
@@ -51,9 +52,14 @@ fn check_duplicated_attr(
         return;
     }
     if let Some(value) = attr.value_str() {
-        emit_if_duplicated(cx, attr, attr_paths, format!("{}:{name}={value}", parent.join(":")));
+        emit_if_duplicated(
+            cx,
+            attr,
+            attr_paths,
+            format!("{}:{name}={value}", parent.iter().join(":")),
+        );
     } else if let Some(sub_attrs) = attr.meta_item_list() {
-        parent.push(name.as_str().to_string());
+        parent.push(name);
         for sub_attr in sub_attrs {
             if let Some(meta) = sub_attr.meta_item() {
                 check_duplicated_attr(cx, meta, attr_paths, parent);
@@ -61,7 +67,7 @@ fn check_duplicated_attr(
         }
         parent.pop();
     } else {
-        emit_if_duplicated(cx, attr, attr_paths, format!("{}:{name}", parent.join(":")));
+        emit_if_duplicated(cx, attr, attr_paths, format!("{}:{name}", parent.iter().join(":")));
     }
 }
 
diff --git a/src/tools/clippy/clippy_lints/src/booleans.rs b/src/tools/clippy/clippy_lints/src/booleans.rs
index 7c6fd91ca67..bf43234ff50 100644
--- a/src/tools/clippy/clippy_lints/src/booleans.rs
+++ b/src/tools/clippy/clippy_lints/src/booleans.rs
@@ -1,10 +1,10 @@
 use clippy_config::Conf;
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
-use clippy_utils::eq_expr_value;
 use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::SpanRangeExt;
 use clippy_utils::sugg::Sugg;
 use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
+use clippy_utils::{eq_expr_value, sym};
 use rustc_ast::ast::LitKind;
 use rustc_attr_data_structures::RustcVersion;
 use rustc_errors::Applicability;
@@ -13,7 +13,7 @@ use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, UnOp};
 use rustc_lint::{LateContext, LateLintPass, Level};
 use rustc_session::impl_lint_pass;
 use rustc_span::def_id::LocalDefId;
-use rustc_span::{Span, SyntaxContext, sym};
+use rustc_span::{Span, Symbol, SyntaxContext};
 
 declare_clippy_lint! {
     /// ### What it does
@@ -73,10 +73,10 @@ declare_clippy_lint! {
 }
 
 // For each pairs, both orders are considered.
-const METHODS_WITH_NEGATION: [(Option<RustcVersion>, &str, &str); 3] = [
-    (None, "is_some", "is_none"),
-    (None, "is_err", "is_ok"),
-    (Some(msrvs::IS_NONE_OR), "is_some_and", "is_none_or"),
+const METHODS_WITH_NEGATION: [(Option<RustcVersion>, Symbol, Symbol); 3] = [
+    (None, sym::is_some, sym::is_none),
+    (None, sym::is_err, sym::is_ok),
+    (Some(msrvs::IS_NONE_OR), sym::is_some_and, sym::is_none_or),
 ];
 
 pub struct NonminimalBool {
@@ -440,9 +440,7 @@ fn simplify_not(cx: &LateContext<'_>, curr_msrv: Msrv, expr: &Expr<'_>) -> Optio
                 .iter()
                 .copied()
                 .flat_map(|(msrv, a, b)| vec![(msrv, a, b), (msrv, b, a)])
-                .find(|&(msrv, a, _)| {
-                    a == path.ident.name.as_str() && msrv.is_none_or(|msrv| curr_msrv.meets(cx, msrv))
-                })
+                .find(|&(msrv, a, _)| a == path.ident.name && msrv.is_none_or(|msrv| curr_msrv.meets(cx, msrv)))
                 .and_then(|(_, _, neg_method)| {
                     let negated_args = args
                         .iter()
diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs b/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs
index 0f066fae118..c1d6cec1b62 100644
--- a/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs
@@ -76,19 +76,20 @@ fn should_lint(cx: &LateContext<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>, msrv: M
         return false;
     }
 
-    match (cast_from.is_integral(), cast_to.is_integral()) {
-        (true, true) => {
+    match (
+        utils::int_ty_to_nbits(cx.tcx, cast_from),
+        utils::int_ty_to_nbits(cx.tcx, cast_to),
+    ) {
+        (Some(from_nbits), Some(to_nbits)) => {
             let cast_signed_to_unsigned = cast_from.is_signed() && !cast_to.is_signed();
-            let from_nbits = utils::int_ty_to_nbits(cast_from, cx.tcx);
-            let to_nbits = utils::int_ty_to_nbits(cast_to, cx.tcx);
             !is_isize_or_usize(cast_from)
                 && !is_isize_or_usize(cast_to)
                 && from_nbits < to_nbits
                 && !cast_signed_to_unsigned
         },
 
-        (true, false) => {
-            let from_nbits = utils::int_ty_to_nbits(cast_from, cx.tcx);
+        (Some(from_nbits), None) => {
+            // FIXME: handle `f16` and `f128`
             let to_nbits = if let ty::Float(FloatTy::F32) = cast_to.kind() {
                 32
             } else {
@@ -96,9 +97,7 @@ fn should_lint(cx: &LateContext<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>, msrv: M
             };
             !is_isize_or_usize(cast_from) && from_nbits < to_nbits
         },
-        (false, true) if matches!(cast_from.kind(), ty::Bool) && msrv.meets(cx, msrvs::FROM_BOOL) => true,
-        (_, _) => {
-            matches!(cast_from.kind(), ty::Float(FloatTy::F32)) && matches!(cast_to.kind(), ty::Float(FloatTy::F64))
-        },
+        (None, Some(_)) if cast_from.is_bool() && msrv.meets(cx, msrvs::FROM_BOOL) => true,
+        _ => matches!(cast_from.kind(), ty::Float(FloatTy::F32)) && matches!(cast_to.kind(), ty::Float(FloatTy::F64)),
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs b/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs
index 4120e5c8cb7..a2ecb5fb44a 100644
--- a/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs
@@ -91,15 +91,14 @@ pub(super) fn check(
     cast_to: Ty<'_>,
     cast_to_span: Span,
 ) {
-    let msg = match (cast_from.kind(), cast_to.is_integral()) {
-        (ty::Int(_) | ty::Uint(_), true) => {
+    let msg = match (cast_from.kind(), utils::int_ty_to_nbits(cx.tcx, cast_to)) {
+        (ty::Int(_) | ty::Uint(_), Some(to_nbits)) => {
             let from_nbits = apply_reductions(
                 cx,
-                utils::int_ty_to_nbits(cast_from, cx.tcx),
+                utils::int_ty_to_nbits(cx.tcx, cast_from).unwrap(),
                 cast_expr,
                 cast_from.is_signed(),
             );
-            let to_nbits = utils::int_ty_to_nbits(cast_to, cx.tcx);
 
             let (should_lint, suffix) = match (is_isize_or_usize(cast_from), is_isize_or_usize(cast_to)) {
                 (true, true) | (false, false) => (to_nbits < from_nbits, ""),
@@ -121,7 +120,7 @@ pub(super) fn check(
             format!("casting `{cast_from}` to `{cast_to}` may truncate the value{suffix}",)
         },
 
-        (ty::Adt(def, _), true) if def.is_enum() => {
+        (ty::Adt(def, _), Some(to_nbits)) if def.is_enum() => {
             let (from_nbits, variant) = if let ExprKind::Path(p) = &cast_expr.kind
                 && let Res::Def(DefKind::Ctor(..), id) = cx.qpath_res(p, cast_expr.hir_id)
             {
@@ -132,7 +131,6 @@ pub(super) fn check(
             } else {
                 (utils::enum_ty_to_nbits(*def, cx.tcx), None)
             };
-            let to_nbits = utils::int_ty_to_nbits(cast_to, cx.tcx);
 
             let cast_from_ptr_size = def.repr().int.is_none_or(|ty| matches!(ty, IntegerType::Pointer(_),));
             let suffix = match (cast_from_ptr_size, is_isize_or_usize(cast_to)) {
@@ -157,11 +155,11 @@ pub(super) fn check(
             format!("casting `{cast_from}` to `{cast_to}` may truncate the value{suffix}")
         },
 
-        (ty::Float(_), true) => {
+        (ty::Float(_), Some(_)) => {
             format!("casting `{cast_from}` to `{cast_to}` may truncate the value")
         },
 
-        (ty::Float(FloatTy::F64), false) if matches!(cast_to.kind(), &ty::Float(FloatTy::F32)) => {
+        (ty::Float(FloatTy::F64), None) if matches!(cast_to.kind(), &ty::Float(FloatTy::F32)) => {
             "casting `f64` to `f32` may truncate the value".to_string()
         },
 
diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_possible_wrap.rs b/src/tools/clippy/clippy_lints/src/casts/cast_possible_wrap.rs
index 504d0a267e4..e26c03ccda9 100644
--- a/src/tools/clippy/clippy_lints/src/casts/cast_possible_wrap.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/cast_possible_wrap.rs
@@ -17,9 +17,12 @@ enum EmitState {
 }
 
 pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) {
-    if !(cast_from.is_integral() && cast_to.is_integral()) {
+    let (Some(from_nbits), Some(to_nbits)) = (
+        utils::int_ty_to_nbits(cx.tcx, cast_from),
+        utils::int_ty_to_nbits(cx.tcx, cast_to),
+    ) else {
         return;
-    }
+    };
 
     // emit a lint if a cast is:
     // 1. unsigned to signed
@@ -35,9 +38,6 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, ca
         return;
     }
 
-    let from_nbits = utils::int_ty_to_nbits(cast_from, cx.tcx);
-    let to_nbits = utils::int_ty_to_nbits(cast_to, cx.tcx);
-
     let should_lint = match (cast_from.is_ptr_sized_integral(), cast_to.is_ptr_sized_integral()) {
         (true, true) => {
             // casts between two ptr sized integers are trivially always the same size
diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_precision_loss.rs b/src/tools/clippy/clippy_lints/src/casts/cast_precision_loss.rs
index 1eb115ce6bd..712e38db499 100644
--- a/src/tools/clippy/clippy_lints/src/casts/cast_precision_loss.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/cast_precision_loss.rs
@@ -7,15 +7,14 @@ use rustc_middle::ty::{self, FloatTy, Ty};
 use super::{CAST_PRECISION_LOSS, utils};
 
 pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) {
-    if !cast_from.is_integral() || cast_to.is_integral() {
+    let Some(from_nbits) = utils::int_ty_to_nbits(cx.tcx, cast_from) else {
         return;
-    }
+    };
 
-    let from_nbits = utils::int_ty_to_nbits(cast_from, cx.tcx);
-    let to_nbits = if cast_to.kind() == &ty::Float(FloatTy::F32) {
-        32
-    } else {
-        64
+    // FIXME: handle `f16` and `f128`
+    let to_nbits = match cast_to.kind() {
+        ty::Float(f @ (FloatTy::F32 | FloatTy::F64)) => f.bit_width(),
+        _ => return,
     };
 
     if !(is_isize_or_usize(cast_from) || from_nbits >= to_nbits) {
@@ -29,9 +28,10 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, ca
     let from_nbits_str = if arch_dependent {
         "64".to_owned()
     } else if is_isize_or_usize(cast_from) {
+        // FIXME: handle 16 bits `usize` type
         "32 or 64".to_owned()
     } else {
-        utils::int_ty_to_nbits(cast_from, cx.tcx).to_string()
+        from_nbits.to_string()
     };
 
     span_lint(
diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs b/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs
index 01020f3eee2..e4dafde0f9d 100644
--- a/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs
@@ -61,7 +61,7 @@ fn is_used_as_unaligned(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
     };
     match parent.kind {
         ExprKind::MethodCall(name, self_arg, ..) if self_arg.hir_id == e.hir_id => {
-            if matches!(name.ident.as_str(), "read_unaligned" | "write_unaligned")
+            if matches!(name.ident.name, sym::read_unaligned | sym::write_unaligned)
                 && let Some(def_id) = cx.typeck_results().type_dependent_def_id(parent.hir_id)
                 && let Some(def_id) = cx.tcx.impl_of_method(def_id)
                 && cx.tcx.type_of(def_id).instantiate_identity().is_raw_ptr()
diff --git a/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast.rs b/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast.rs
index ac1a355c8d9..105477093b5 100644
--- a/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast.rs
@@ -3,24 +3,22 @@ use clippy_utils::source::snippet_with_applicability;
 use rustc_errors::Applicability;
 use rustc_hir::Expr;
 use rustc_lint::LateContext;
-use rustc_middle::ty::{self, Ty, UintTy};
+use rustc_middle::ty::{self, Ty};
 
 use super::{FN_TO_NUMERIC_CAST, utils};
 
 pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) {
     // We only want to check casts to `ty::Uint` or `ty::Int`
-    match cast_to.kind() {
-        ty::Uint(_) | ty::Int(..) => { /* continue on */ },
-        _ => return,
-    }
+    let Some(to_nbits) = utils::int_ty_to_nbits(cx.tcx, cast_to) else {
+        return;
+    };
 
     match cast_from.kind() {
         ty::FnDef(..) | ty::FnPtr(..) => {
             let mut applicability = Applicability::MaybeIncorrect;
-            let from_snippet = snippet_with_applicability(cx, cast_expr.span, "x", &mut applicability);
-            let to_nbits = utils::int_ty_to_nbits(cast_to, cx.tcx);
 
-            if (to_nbits >= cx.tcx.data_layout.pointer_size.bits()) && (*cast_to.kind() != ty::Uint(UintTy::Usize)) {
+            if to_nbits >= cx.tcx.data_layout.pointer_size.bits() && !cast_to.is_usize() {
+                let from_snippet = snippet_with_applicability(cx, cast_expr.span, "x", &mut applicability);
                 span_lint_and_sugg(
                     cx,
                     FN_TO_NUMERIC_CAST,
diff --git a/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast_with_truncation.rs b/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast_with_truncation.rs
index 18e7798452e..700b7d0d426 100644
--- a/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast_with_truncation.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast_with_truncation.rs
@@ -9,16 +9,14 @@ use super::{FN_TO_NUMERIC_CAST_WITH_TRUNCATION, utils};
 
 pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) {
     // We only want to check casts to `ty::Uint` or `ty::Int`
-    match cast_to.kind() {
-        ty::Uint(_) | ty::Int(..) => { /* continue on */ },
-        _ => return,
-    }
+    let Some(to_nbits) = utils::int_ty_to_nbits(cx.tcx, cast_to) else {
+        return;
+    };
     match cast_from.kind() {
         ty::FnDef(..) | ty::FnPtr(..) => {
             let mut applicability = Applicability::MaybeIncorrect;
             let from_snippet = snippet_with_applicability(cx, cast_expr.span, "x", &mut applicability);
 
-            let to_nbits = utils::int_ty_to_nbits(cast_to, cx.tcx);
             if to_nbits < cx.tcx.data_layout.pointer_size.bits() {
                 span_lint_and_sugg(
                     cx,
diff --git a/src/tools/clippy/clippy_lints/src/casts/utils.rs b/src/tools/clippy/clippy_lints/src/casts/utils.rs
index 5ccba92a0af..318a1646477 100644
--- a/src/tools/clippy/clippy_lints/src/casts/utils.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/utils.rs
@@ -1,27 +1,14 @@
 use clippy_utils::ty::{EnumValue, read_explicit_enum_value};
 use rustc_middle::ty::{self, AdtDef, IntTy, Ty, TyCtxt, UintTy, VariantDiscr};
 
-/// Returns the size in bits of an integral type.
-/// Will return 0 if the type is not an int or uint variant
-pub(super) fn int_ty_to_nbits(typ: Ty<'_>, tcx: TyCtxt<'_>) -> u64 {
-    match typ.kind() {
-        ty::Int(i) => match i {
-            IntTy::Isize => tcx.data_layout.pointer_size.bits(),
-            IntTy::I8 => 8,
-            IntTy::I16 => 16,
-            IntTy::I32 => 32,
-            IntTy::I64 => 64,
-            IntTy::I128 => 128,
-        },
-        ty::Uint(i) => match i {
-            UintTy::Usize => tcx.data_layout.pointer_size.bits(),
-            UintTy::U8 => 8,
-            UintTy::U16 => 16,
-            UintTy::U32 => 32,
-            UintTy::U64 => 64,
-            UintTy::U128 => 128,
-        },
-        _ => 0,
+/// Returns the size in bits of an integral type, or `None` if `ty` is not an
+/// integral type.
+pub(super) fn int_ty_to_nbits(tcx: TyCtxt<'_>, ty: Ty<'_>) -> Option<u64> {
+    match ty.kind() {
+        ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize) => Some(tcx.data_layout.pointer_size.bits()),
+        ty::Int(i) => i.bit_width(),
+        ty::Uint(i) => i.bit_width(),
+        _ => None,
     }
 }
 
diff --git a/src/tools/clippy/clippy_lints/src/checked_conversions.rs b/src/tools/clippy/clippy_lints/src/checked_conversions.rs
index 8ada608049c..9b3822f9d8f 100644
--- a/src/tools/clippy/clippy_lints/src/checked_conversions.rs
+++ b/src/tools/clippy/clippy_lints/src/checked_conversions.rs
@@ -2,11 +2,12 @@ use clippy_config::Conf;
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::{SpanlessEq, is_in_const_context, is_integer_literal};
+use clippy_utils::{SpanlessEq, is_in_const_context, is_integer_literal, sym};
 use rustc_errors::Applicability;
 use rustc_hir::{BinOpKind, Expr, ExprKind, QPath, TyKind};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_session::impl_lint_pass;
+use rustc_span::Symbol;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -98,7 +99,7 @@ impl LateLintPass<'_> for CheckedConversions {
 struct Conversion<'a> {
     cvt: ConversionType,
     expr_to_cast: &'a Expr<'a>,
-    to_type: Option<&'a str>,
+    to_type: Option<Symbol>,
 }
 
 /// The kind of conversion that is checked
@@ -150,7 +151,7 @@ impl<'a> Conversion<'a> {
     }
 
     /// Try to construct a new conversion if the conversion type is valid
-    fn try_new(expr_to_cast: &'a Expr<'_>, from_type: &str, to_type: &'a str) -> Option<Conversion<'a>> {
+    fn try_new(expr_to_cast: &'a Expr<'_>, from_type: Symbol, to_type: Symbol) -> Option<Conversion<'a>> {
         ConversionType::try_new(from_type, to_type).map(|cvt| Conversion {
             cvt,
             expr_to_cast,
@@ -171,7 +172,7 @@ impl<'a> Conversion<'a> {
 impl ConversionType {
     /// Creates a conversion type if the type is allowed & conversion is valid
     #[must_use]
-    fn try_new(from: &str, to: &str) -> Option<Self> {
+    fn try_new(from: Symbol, to: Symbol) -> Option<Self> {
         if UINTS.contains(&from) {
             Some(Self::FromUnsigned)
         } else if SINTS.contains(&from) {
@@ -190,7 +191,7 @@ impl ConversionType {
 
 /// Check for `expr <= (to_type::MAX as from_type)`
 fn check_upper_bound<'tcx>(lt: &'tcx Expr<'tcx>, gt: &'tcx Expr<'tcx>) -> Option<Conversion<'tcx>> {
-    if let Some((from, to)) = get_types_from_cast(gt, INTS, "max_value", "MAX") {
+    if let Some((from, to)) = get_types_from_cast(gt, INTS, sym::max_value, sym::MAX) {
         Conversion::try_new(lt, from, to)
     } else {
         None
@@ -209,7 +210,7 @@ fn check_lower_bound_zero<'a>(candidate: &'a Expr<'_>, check: &'a Expr<'_>) -> O
 
 /// Check for `expr >= (to_type::MIN as from_type)`
 fn check_lower_bound_min<'a>(candidate: &'a Expr<'_>, check: &'a Expr<'_>) -> Option<Conversion<'a>> {
-    if let Some((from, to)) = get_types_from_cast(check, SINTS, "min_value", "MIN") {
+    if let Some((from, to)) = get_types_from_cast(check, SINTS, sym::min_value, sym::MIN) {
         Conversion::try_new(candidate, from, to)
     } else {
         None
@@ -217,15 +218,15 @@ fn check_lower_bound_min<'a>(candidate: &'a Expr<'_>, check: &'a Expr<'_>) -> Op
 }
 
 /// Tries to extract the from- and to-type from a cast expression
-fn get_types_from_cast<'a>(
-    expr: &'a Expr<'_>,
-    types: &'a [&str],
-    func: &'a str,
-    assoc_const: &'a str,
-) -> Option<(&'a str, &'a str)> {
+fn get_types_from_cast(
+    expr: &Expr<'_>,
+    types: &[Symbol],
+    func: Symbol,
+    assoc_const: Symbol,
+) -> Option<(Symbol, Symbol)> {
     // `to_type::max_value() as from_type`
     // or `to_type::MAX as from_type`
-    let call_from_cast: Option<(&Expr<'_>, &str)> = if let ExprKind::Cast(limit, from_type) = &expr.kind
+    let call_from_cast: Option<(&Expr<'_>, Symbol)> = if let ExprKind::Cast(limit, from_type) = &expr.kind
         // to_type::max_value(), from_type
         && let TyKind::Path(from_type_path) = &from_type.kind
         && let Some(from_sym) = int_ty_to_sym(from_type_path)
@@ -236,12 +237,12 @@ fn get_types_from_cast<'a>(
     };
 
     // `from_type::from(to_type::max_value())`
-    let limit_from: Option<(&Expr<'_>, &str)> = call_from_cast.or_else(|| {
+    let limit_from: Option<(&Expr<'_>, Symbol)> = call_from_cast.or_else(|| {
         if let ExprKind::Call(from_func, [limit]) = &expr.kind
             // `from_type::from, to_type::max_value()`
             // `from_type::from`
             && let ExprKind::Path(path) = &from_func.kind
-            && let Some(from_sym) = get_implementing_type(path, INTS, "from")
+            && let Some(from_sym) = get_implementing_type(path, INTS, sym::from)
         {
             Some((limit, from_sym))
         } else {
@@ -273,32 +274,41 @@ fn get_types_from_cast<'a>(
 }
 
 /// Gets the type which implements the called function
-fn get_implementing_type<'a>(path: &QPath<'_>, candidates: &'a [&str], function: &str) -> Option<&'a str> {
+fn get_implementing_type(path: &QPath<'_>, candidates: &[Symbol], function: Symbol) -> Option<Symbol> {
     if let QPath::TypeRelative(ty, path) = &path
-        && path.ident.name.as_str() == function
+        && path.ident.name == function
         && let TyKind::Path(QPath::Resolved(None, tp)) = &ty.kind
         && let [int] = tp.segments
     {
-        let name = int.ident.name.as_str();
-        candidates.iter().find(|c| &name == *c).copied()
+        candidates.iter().find(|c| int.ident.name == **c).copied()
     } else {
         None
     }
 }
 
 /// Gets the type as a string, if it is a supported integer
-fn int_ty_to_sym<'tcx>(path: &QPath<'_>) -> Option<&'tcx str> {
+fn int_ty_to_sym(path: &QPath<'_>) -> Option<Symbol> {
     if let QPath::Resolved(_, path) = *path
         && let [ty] = path.segments
     {
-        let name = ty.ident.name.as_str();
-        INTS.iter().find(|c| &name == *c).copied()
+        INTS.iter().find(|c| ty.ident.name == **c).copied()
     } else {
         None
     }
 }
 
 // Constants
-const UINTS: &[&str] = &["u8", "u16", "u32", "u64", "usize"];
-const SINTS: &[&str] = &["i8", "i16", "i32", "i64", "isize"];
-const INTS: &[&str] = &["u8", "u16", "u32", "u64", "usize", "i8", "i16", "i32", "i64", "isize"];
+const UINTS: &[Symbol] = &[sym::u8, sym::u16, sym::u32, sym::u64, sym::usize];
+const SINTS: &[Symbol] = &[sym::i8, sym::i16, sym::i32, sym::i64, sym::isize];
+const INTS: &[Symbol] = &[
+    sym::u8,
+    sym::u16,
+    sym::u32,
+    sym::u64,
+    sym::usize,
+    sym::i8,
+    sym::i16,
+    sym::i32,
+    sym::i64,
+    sym::isize,
+];
diff --git a/src/tools/clippy/clippy_lints/src/cloned_ref_to_slice_refs.rs b/src/tools/clippy/clippy_lints/src/cloned_ref_to_slice_refs.rs
index 6b239a1541b..e33a8e0fb74 100644
--- a/src/tools/clippy/clippy_lints/src/cloned_ref_to_slice_refs.rs
+++ b/src/tools/clippy/clippy_lints/src/cloned_ref_to_slice_refs.rs
@@ -17,7 +17,7 @@ declare_clippy_lint! {
     ///
     /// ### Why is this bad
     ///
-    /// A reference does not need to be owned in order to used as a slice.
+    /// A reference does not need to be owned in order to be used as a slice.
     ///
     /// ### Known problems
     ///
diff --git a/src/tools/clippy/clippy_lints/src/coerce_container_to_any.rs b/src/tools/clippy/clippy_lints/src/coerce_container_to_any.rs
new file mode 100644
index 00000000000..2b659253763
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/coerce_container_to_any.rs
@@ -0,0 +1,108 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet;
+use clippy_utils::sym;
+use rustc_errors::Applicability;
+use rustc_hir::{Expr, ExprKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty::{self, ExistentialPredicate, Ty, TyCtxt};
+use rustc_session::declare_lint_pass;
+
+declare_clippy_lint! {
+    /// ### What it does
+    ///
+    /// Protects against unintended coercion of references to container types to `&dyn Any` when the
+    /// container type dereferences to a `dyn Any` which could be directly referenced instead.
+    ///
+    /// ### Why is this bad?
+    ///
+    /// The intention is usually to get a reference to the `dyn Any` the value dereferences to,
+    /// rather than coercing a reference to the container itself to `&dyn Any`.
+    ///
+    /// ### Example
+    ///
+    /// Because `Box<dyn Any>` itself implements `Any`, `&Box<dyn Any>`
+    /// can be coerced to an `&dyn Any` which refers to *the `Box` itself*, rather than the
+    /// inner `dyn Any`.
+    /// ```no_run
+    /// # use std::any::Any;
+    /// let x: Box<dyn Any> = Box::new(0u32);
+    /// let dyn_any_of_box: &dyn Any = &x;
+    ///
+    /// // Fails as we have a &dyn Any to the Box, not the u32
+    /// assert_eq!(dyn_any_of_box.downcast_ref::<u32>(), None);
+    /// ```
+    /// Use instead:
+    /// ```no_run
+    /// # use std::any::Any;
+    /// let x: Box<dyn Any> = Box::new(0u32);
+    /// let dyn_any_of_u32: &dyn Any = &*x;
+    ///
+    /// // Succeeds since we have a &dyn Any to the inner u32!
+    /// assert_eq!(dyn_any_of_u32.downcast_ref::<u32>(), Some(&0u32));
+    /// ```
+    #[clippy::version = "1.88.0"]
+    pub COERCE_CONTAINER_TO_ANY,
+    nursery,
+    "coercing to `&dyn Any` when dereferencing could produce a `dyn Any` without coercion is usually not intended"
+}
+declare_lint_pass!(CoerceContainerToAny => [COERCE_CONTAINER_TO_ANY]);
+
+impl<'tcx> LateLintPass<'tcx> for CoerceContainerToAny {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
+        // If this expression has an effective type of `&dyn Any` ...
+        {
+            let coerced_ty = cx.typeck_results().expr_ty_adjusted(e);
+
+            let ty::Ref(_, coerced_ref_ty, _) = *coerced_ty.kind() else {
+                return;
+            };
+            if !is_dyn_any(cx.tcx, coerced_ref_ty) {
+                return;
+            }
+        }
+
+        let expr_ty = cx.typeck_results().expr_ty(e);
+        let ty::Ref(_, expr_ref_ty, _) = *expr_ty.kind() else {
+            return;
+        };
+        // ... but only due to coercion ...
+        if is_dyn_any(cx.tcx, expr_ref_ty) {
+            return;
+        }
+        // ... and it also *derefs* to `dyn Any` ...
+        let Some((depth, target)) = clippy_utils::ty::deref_chain(cx, expr_ref_ty).enumerate().last() else {
+            return;
+        };
+        if !is_dyn_any(cx.tcx, target) {
+            return;
+        }
+
+        // ... that's probably not intended.
+        let (span, deref_count) = match e.kind {
+            // If `e` was already an `&` expression, skip `*&` in the suggestion
+            ExprKind::AddrOf(_, _, referent) => (referent.span, depth),
+            _ => (e.span, depth + 1),
+        };
+        span_lint_and_sugg(
+            cx,
+            COERCE_CONTAINER_TO_ANY,
+            e.span,
+            format!("coercing `{expr_ty}` to `&dyn Any`"),
+            "consider dereferencing",
+            format!("&{}{}", str::repeat("*", deref_count), snippet(cx, span, "x")),
+            Applicability::MaybeIncorrect,
+        );
+    }
+}
+
+fn is_dyn_any(tcx: TyCtxt<'_>, ty: Ty<'_>) -> bool {
+    let ty::Dynamic(traits, ..) = ty.kind() else {
+        return false;
+    };
+    traits.iter().any(|binder| {
+        let ExistentialPredicate::Trait(t) = binder.skip_binder() else {
+            return false;
+        };
+        tcx.is_diagnostic_item(sym::Any, t.def_id)
+    })
+}
diff --git a/src/tools/clippy/clippy_lints/src/copies.rs b/src/tools/clippy/clippy_lints/src/copies.rs
index 2467fc95fd0..5ef726638a5 100644
--- a/src/tools/clippy/clippy_lints/src/copies.rs
+++ b/src/tools/clippy/clippy_lints/src/copies.rs
@@ -425,7 +425,9 @@ fn scan_block_for_eq<'tcx>(
             modifies_any_local(cx, stmt, &cond_locals)
                 || !eq_stmts(stmt, blocks, |b| b.stmts.get(i), &mut eq, &mut moved_locals)
         })
-        .map_or(block.stmts.len(), |(i, _)| i);
+        .map_or(block.stmts.len(), |(i, stmt)| {
+            adjust_by_closest_callsite(i, stmt, block.stmts[..i].iter().enumerate().rev())
+        });
 
     if local_needs_ordered_drop {
         return BlockEq {
@@ -467,7 +469,9 @@ fn scan_block_for_eq<'tcx>(
                     .is_none_or(|s| hash != hash_stmt(cx, s))
             })
         })
-        .map_or(block.stmts.len() - start_end_eq, |(i, _)| i);
+        .map_or(block.stmts.len() - start_end_eq, |(i, stmt)| {
+            adjust_by_closest_callsite(i, stmt, (0..i).rev().zip(block.stmts[(block.stmts.len() - i)..].iter()))
+        });
 
     let moved_locals_at_start = moved_locals.len();
     let mut i = end_search_start;
@@ -522,6 +526,49 @@ fn scan_block_for_eq<'tcx>(
     }
 }
 
+/// Adjusts the index for which the statements begin to differ to the closest macro callsite. This
+/// avoids giving suggestions that requires splitting a macro call in half, when only a part of the
+/// macro expansion is equal.
+///
+/// For example, for the following macro:
+/// ```rust,ignore
+/// macro_rules! foo {
+///    ($x:expr) => {
+///        let y = 42;
+///        $x;
+///    };
+/// }
+/// ```
+/// If the macro is called like this:
+/// ```rust,ignore
+/// if false {
+///    let z = 42;
+///    foo!(println!("Hello"));
+/// } else {
+///    let z = 42;
+///    foo!(println!("World"));
+/// }
+/// ```
+/// Although the expanded `let y = 42;` is equal, the macro call should not be included in the
+/// suggestion.
+fn adjust_by_closest_callsite<'tcx>(
+    i: usize,
+    stmt: &'tcx Stmt<'tcx>,
+    mut iter: impl Iterator<Item = (usize, &'tcx Stmt<'tcx>)>,
+) -> usize {
+    let Some((_, first)) = iter.next() else {
+        return 0;
+    };
+
+    // If it is already at the boundary of a macro call, then just return.
+    if first.span.source_callsite() != stmt.span.source_callsite() {
+        return i;
+    }
+
+    iter.find(|(_, stmt)| stmt.span.source_callsite() != first.span.source_callsite())
+        .map_or(0, |(i, _)| i + 1)
+}
+
 fn check_for_warn_of_moved_symbol(cx: &LateContext<'_>, symbols: &[(HirId, Symbol)], if_expr: &Expr<'_>) -> bool {
     get_enclosing_block(cx, if_expr.hir_id).is_some_and(|block| {
         let ignore_span = block.span.shrink_to_lo().to(if_expr.span);
diff --git a/src/tools/clippy/clippy_lints/src/create_dir.rs b/src/tools/clippy/clippy_lints/src/create_dir.rs
index b43906903a0..695b25aeb0b 100644
--- a/src/tools/clippy/clippy_lints/src/create_dir.rs
+++ b/src/tools/clippy/clippy_lints/src/create_dir.rs
@@ -1,7 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::source::snippet_with_applicability;
 use rustc_errors::Applicability;
-use rustc_hir::{Expr, ExprKind};
+use rustc_hir::{Expr, ExprKind, QPath};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
 use rustc_span::sym;
@@ -34,10 +33,12 @@ declare_lint_pass!(CreateDir => [CREATE_DIR]);
 
 impl LateLintPass<'_> for CreateDir {
     fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
-        if let ExprKind::Call(func, [arg]) = expr.kind
+        if let ExprKind::Call(func, [_]) = expr.kind
             && let ExprKind::Path(ref path) = func.kind
             && let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id()
             && cx.tcx.is_diagnostic_item(sym::fs_create_dir, def_id)
+            && let QPath::Resolved(_, path) = path
+            && let Some(last) = path.segments.last()
         {
             span_lint_and_then(
                 cx,
@@ -45,15 +46,15 @@ impl LateLintPass<'_> for CreateDir {
                 expr.span,
                 "calling `std::fs::create_dir` where there may be a better way",
                 |diag| {
-                    let mut app = Applicability::MaybeIncorrect;
-                    diag.span_suggestion_verbose(
-                        expr.span,
+                    let mut suggestions = vec![(last.ident.span.shrink_to_hi(), "_all".to_owned())];
+                    if path.segments.len() == 1 {
+                        suggestions.push((path.span.shrink_to_lo(), "std::fs::".to_owned()));
+                    }
+
+                    diag.multipart_suggestion_verbose(
                         "consider calling `std::fs::create_dir_all` instead",
-                        format!(
-                            "create_dir_all({})",
-                            snippet_with_applicability(cx, arg.span, "..", &mut app)
-                        ),
-                        app,
+                        suggestions,
+                        Applicability::MaybeIncorrect,
                     );
                 },
             );
diff --git a/src/tools/clippy/clippy_lints/src/ctfe.rs b/src/tools/clippy/clippy_lints/src/ctfe.rs
deleted file mode 100644
index 7bae04a10f1..00000000000
--- a/src/tools/clippy/clippy_lints/src/ctfe.rs
+++ /dev/null
@@ -1,26 +0,0 @@
-use rustc_hir::def_id::LocalDefId;
-use rustc_hir::intravisit::FnKind;
-use rustc_hir::{Body, FnDecl};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::declare_lint_pass;
-use rustc_span::Span;
-
-declare_lint_pass! {
-    /// Ensures that Constant-time Function Evaluation is being done (specifically, MIR lint passes).
-    /// As Clippy deactivates codegen, this lint ensures that CTFE (used in hard errors) is still ran.
-    ClippyCtfe => []
-}
-
-impl<'tcx> LateLintPass<'tcx> for ClippyCtfe {
-    fn check_fn(
-        &mut self,
-        cx: &LateContext<'_>,
-        _: FnKind<'tcx>,
-        _: &'tcx FnDecl<'tcx>,
-        _: &'tcx Body<'tcx>,
-        _: Span,
-        defid: LocalDefId,
-    ) {
-        cx.tcx.ensure_ok().mir_drops_elaborated_and_const_checked(defid); // Lint
-    }
-}
diff --git a/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs
index 5fcb851dfeb..1e3907d9ede 100644
--- a/src/tools/clippy/clippy_lints/src/declared_lints.rs
+++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs
@@ -77,6 +77,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
     crate::cfg_not_test::CFG_NOT_TEST_INFO,
     crate::checked_conversions::CHECKED_CONVERSIONS_INFO,
     crate::cloned_ref_to_slice_refs::CLONED_REF_TO_SLICE_REFS_INFO,
+    crate::coerce_container_to_any::COERCE_CONTAINER_TO_ANY_INFO,
     crate::cognitive_complexity::COGNITIVE_COMPLEXITY_INFO,
     crate::collapsible_if::COLLAPSIBLE_ELSE_IF_INFO,
     crate::collapsible_if::COLLAPSIBLE_IF_INFO,
@@ -119,6 +120,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
     crate::doc::DOC_MARKDOWN_INFO,
     crate::doc::DOC_NESTED_REFDEFS_INFO,
     crate::doc::DOC_OVERINDENTED_LIST_ITEMS_INFO,
+    crate::doc::DOC_SUSPICIOUS_FOOTNOTES_INFO,
     crate::doc::EMPTY_DOCS_INFO,
     crate::doc::MISSING_ERRORS_DOC_INFO,
     crate::doc::MISSING_PANICS_DOC_INFO,
@@ -166,6 +168,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
     crate::floating_point_arithmetic::SUBOPTIMAL_FLOPS_INFO,
     crate::format::USELESS_FORMAT_INFO,
     crate::format_args::FORMAT_IN_FORMAT_ARGS_INFO,
+    crate::format_args::POINTER_FORMAT_INFO,
     crate::format_args::TO_STRING_IN_FORMAT_ARGS_INFO,
     crate::format_args::UNINLINED_FORMAT_ARGS_INFO,
     crate::format_args::UNNECESSARY_DEBUG_FORMATTING_INFO,
@@ -211,6 +214,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
     crate::indexing_slicing::INDEXING_SLICING_INFO,
     crate::indexing_slicing::OUT_OF_BOUNDS_INDEXING_INFO,
     crate::ineffective_open_options::INEFFECTIVE_OPEN_OPTIONS_INFO,
+    crate::infallible_try_from::INFALLIBLE_TRY_FROM_INFO,
     crate::infinite_iter::INFINITE_ITER_INFO,
     crate::infinite_iter::MAYBE_INFINITE_ITER_INFO,
     crate::inherent_impl::MULTIPLE_INHERENT_IMPL_INFO,
@@ -379,6 +383,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
     crate::methods::INSPECT_FOR_EACH_INFO,
     crate::methods::INTO_ITER_ON_REF_INFO,
     crate::methods::IO_OTHER_ERROR_INFO,
+    crate::methods::IP_CONSTANT_INFO,
     crate::methods::IS_DIGIT_ASCII_RADIX_INFO,
     crate::methods::ITERATOR_STEP_BY_ZERO_INFO,
     crate::methods::ITER_CLONED_COLLECT_INFO,
diff --git a/src/tools/clippy/clippy_lints/src/default.rs b/src/tools/clippy/clippy_lints/src/default.rs
index 886c325b355..a48e4d2fbd5 100644
--- a/src/tools/clippy/clippy_lints/src/default.rs
+++ b/src/tools/clippy/clippy_lints/src/default.rs
@@ -1,10 +1,9 @@
 use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_sugg};
 use clippy_utils::source::snippet_with_context;
 use clippy_utils::ty::{has_drop, is_copy};
-use clippy_utils::{contains_name, get_parent_expr, in_automatically_derived, is_from_proc_macro};
+use clippy_utils::{contains_name, get_parent_expr, in_automatically_derived, is_expr_default, is_from_proc_macro};
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::Applicability;
-use rustc_hir::def::Res;
 use rustc_hir::{Block, Expr, ExprKind, PatKind, QPath, Stmt, StmtKind, StructTailExpr};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty;
@@ -129,7 +128,7 @@ impl<'tcx> LateLintPass<'tcx> for Default {
                 // only take bindings to identifiers
                 && let PatKind::Binding(_, binding_id, ident, _) = local.pat.kind
                 // only when assigning `... = Default::default()`
-                && is_expr_default(expr, cx)
+                && is_expr_default(cx, expr)
                 && let binding_type = cx.typeck_results().node_type(binding_id)
                 && let ty::Adt(adt, args) = *binding_type.kind()
                 && adt.is_struct()
@@ -251,19 +250,6 @@ impl<'tcx> LateLintPass<'tcx> for Default {
     }
 }
 
-/// Checks if the given expression is the `default` method belonging to the `Default` trait.
-fn is_expr_default<'tcx>(expr: &'tcx Expr<'tcx>, cx: &LateContext<'tcx>) -> bool {
-    if let ExprKind::Call(fn_expr, []) = &expr.kind
-        && let ExprKind::Path(qpath) = &fn_expr.kind
-        && let Res::Def(_, def_id) = cx.qpath_res(qpath, fn_expr.hir_id)
-    {
-        // right hand side of assignment is `Default::default`
-        cx.tcx.is_diagnostic_item(sym::default_fn, def_id)
-    } else {
-        false
-    }
-}
-
 /// Returns the reassigned field and the assigning expression (right-hand side of assign).
 fn field_reassigned_by_stmt<'tcx>(this: &Stmt<'tcx>, binding_name: Symbol) -> Option<(Ident, &'tcx Expr<'tcx>)> {
     if let StmtKind::Semi(later_expr) = this.kind
diff --git a/src/tools/clippy/clippy_lints/src/disallowed_names.rs b/src/tools/clippy/clippy_lints/src/disallowed_names.rs
index f55b0cf1c50..566aa12b08f 100644
--- a/src/tools/clippy/clippy_lints/src/disallowed_names.rs
+++ b/src/tools/clippy/clippy_lints/src/disallowed_names.rs
@@ -1,6 +1,6 @@
 use clippy_config::Conf;
 use clippy_utils::diagnostics::span_lint;
-use clippy_utils::is_in_test;
+use clippy_utils::{is_from_proc_macro, is_in_test};
 use rustc_data_structures::fx::FxHashSet;
 use rustc_hir::{Pat, PatKind};
 use rustc_lint::{LateContext, LateLintPass};
@@ -43,8 +43,10 @@ impl_lint_pass!(DisallowedNames => [DISALLOWED_NAMES]);
 impl<'tcx> LateLintPass<'tcx> for DisallowedNames {
     fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) {
         if let PatKind::Binding(.., ident, _) = pat.kind
+            && !ident.span.from_expansion()
             && self.disallow.contains(&ident.name)
             && !is_in_test(cx.tcx, pat.hir_id)
+            && !is_from_proc_macro(cx, &ident)
         {
             span_lint(
                 cx,
diff --git a/src/tools/clippy/clippy_lints/src/disallowed_types.rs b/src/tools/clippy/clippy_lints/src/disallowed_types.rs
index 821bb25d2ce..7875cdd77e8 100644
--- a/src/tools/clippy/clippy_lints/src/disallowed_types.rs
+++ b/src/tools/clippy/clippy_lints/src/disallowed_types.rs
@@ -105,10 +105,10 @@ impl_lint_pass!(DisallowedTypes => [DISALLOWED_TYPES]);
 
 impl<'tcx> LateLintPass<'tcx> for DisallowedTypes {
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
-        if let ItemKind::Use(path, UseKind::Single(_)) = &item.kind {
-            if let Some(res) = path.res.type_ns {
-                self.check_res_emit(cx, &res, item.span);
-            }
+        if let ItemKind::Use(path, UseKind::Single(_)) = &item.kind
+            && let Some(res) = path.res.type_ns
+        {
+            self.check_res_emit(cx, &res, item.span);
         }
     }
 
diff --git a/src/tools/clippy/clippy_lints/src/doc/doc_suspicious_footnotes.rs b/src/tools/clippy/clippy_lints/src/doc/doc_suspicious_footnotes.rs
new file mode 100644
index 00000000000..289b6b915d4
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/doc/doc_suspicious_footnotes.rs
@@ -0,0 +1,113 @@
+use clippy_utils::diagnostics::span_lint_and_then;
+use rustc_ast::token::CommentKind;
+use rustc_errors::Applicability;
+use rustc_hir::{AttrStyle, Attribute};
+use rustc_lint::{LateContext, LintContext};
+use rustc_resolve::rustdoc::DocFragmentKind;
+
+use std::ops::Range;
+
+use super::{DOC_SUSPICIOUS_FOOTNOTES, Fragments};
+
+pub fn check(cx: &LateContext<'_>, doc: &str, range: Range<usize>, fragments: &Fragments<'_>, attrs: &[Attribute]) {
+    for i in doc[range.clone()]
+        .bytes()
+        .enumerate()
+        .filter_map(|(i, c)| if c == b'[' { Some(i) } else { None })
+    {
+        let start = i + range.start;
+        if doc.as_bytes().get(start + 1) == Some(&b'^')
+            && let Some(end) = all_numbers_upto_brace(doc, start + 2)
+            && doc.as_bytes().get(end) != Some(&b':')
+            && doc.as_bytes().get(start - 1) != Some(&b'\\')
+            && let Some(this_fragment) = {
+                // the `doc` string contains all fragments concatenated together
+                // figure out which one this suspicious footnote comes from
+                let mut starting_position = 0;
+                let mut found_fragment = fragments.fragments.last();
+                for fragment in fragments.fragments {
+                    if start >= starting_position && start < starting_position + fragment.doc.as_str().len() {
+                        found_fragment = Some(fragment);
+                        break;
+                    }
+                    starting_position += fragment.doc.as_str().len();
+                }
+                found_fragment
+            }
+        {
+            let span = fragments.span(cx, start..end).unwrap_or(this_fragment.span);
+            span_lint_and_then(
+                cx,
+                DOC_SUSPICIOUS_FOOTNOTES,
+                span,
+                "looks like a footnote ref, but has no matching footnote",
+                |diag| {
+                    if this_fragment.kind == DocFragmentKind::SugaredDoc {
+                        let (doc_attr, (_, doc_attr_comment_kind)) = attrs
+                            .iter()
+                            .filter(|attr| attr.span().overlaps(this_fragment.span))
+                            .rev()
+                            .find_map(|attr| Some((attr, attr.doc_str_and_comment_kind()?)))
+                            .unwrap();
+                        let (to_add, terminator) = match (doc_attr_comment_kind, doc_attr.style()) {
+                            (CommentKind::Line, AttrStyle::Outer) => ("\n///\n/// ", ""),
+                            (CommentKind::Line, AttrStyle::Inner) => ("\n//!\n//! ", ""),
+                            (CommentKind::Block, AttrStyle::Outer) => ("\n/** ", " */"),
+                            (CommentKind::Block, AttrStyle::Inner) => ("\n/*! ", " */"),
+                        };
+                        diag.span_suggestion_verbose(
+                            doc_attr.span().shrink_to_hi(),
+                            "add footnote definition",
+                            format!(
+                                "{to_add}{label}: <!-- description -->{terminator}",
+                                label = &doc[start..end]
+                            ),
+                            Applicability::HasPlaceholders,
+                        );
+                    } else {
+                        let is_file_include = cx
+                            .sess()
+                            .source_map()
+                            .span_to_snippet(this_fragment.span)
+                            .as_ref()
+                            .map(|vdoc| vdoc.trim())
+                            == Ok(doc);
+                        if is_file_include {
+                            // if this is a file include, then there's no quote marks
+                            diag.span_suggestion_verbose(
+                                this_fragment.span.shrink_to_hi(),
+                                "add footnote definition",
+                                format!("\n\n{label}: <!-- description -->", label = &doc[start..end],),
+                                Applicability::HasPlaceholders,
+                            );
+                        } else {
+                            // otherwise, we wrap in a string
+                            diag.span_suggestion_verbose(
+                                this_fragment.span,
+                                "add footnote definition",
+                                format!(
+                                    "r#\"{doc}\n\n{label}: <!-- description -->\"#",
+                                    doc = this_fragment.doc,
+                                    label = &doc[start..end],
+                                ),
+                                Applicability::HasPlaceholders,
+                            );
+                        }
+                    }
+                },
+            );
+        }
+    }
+}
+
+fn all_numbers_upto_brace(text: &str, i: usize) -> Option<usize> {
+    for (j, c) in text.as_bytes()[i..].iter().copied().enumerate().take(64) {
+        if c == b']' && j != 0 {
+            return Some(i + j + 1);
+        }
+        if !c.is_ascii_digit() || j >= 64 {
+            break;
+        }
+    }
+    None
+}
diff --git a/src/tools/clippy/clippy_lints/src/doc/mod.rs b/src/tools/clippy/clippy_lints/src/doc/mod.rs
index c46dd09d60c..e0fc2fd9347 100644
--- a/src/tools/clippy/clippy_lints/src/doc/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/doc/mod.rs
@@ -25,6 +25,7 @@ use std::ops::Range;
 use url::Url;
 
 mod doc_comment_double_space_linebreaks;
+mod doc_suspicious_footnotes;
 mod include_in_doc_without_cfg;
 mod lazy_continuation;
 mod link_with_quotes;
@@ -607,6 +608,37 @@ declare_clippy_lint! {
     "double-space used for doc comment linebreak instead of `\\`"
 }
 
+declare_clippy_lint! {
+    /// ### What it does
+    /// Detects syntax that looks like a footnote reference.
+    ///
+    /// Rustdoc footnotes are compatible with GitHub-Flavored Markdown (GFM).
+    /// GFM does not parse a footnote reference unless its definition also
+    /// exists. This lint checks for footnote references with missing
+    /// definitions, unless it thinks you're writing a regex.
+    ///
+    /// ### Why is this bad?
+    /// This probably means that a footnote was meant to exist,
+    /// but was not written.
+    ///
+    /// ### Example
+    /// ```no_run
+    /// /// This is not a footnote[^1], because no definition exists.
+    /// fn my_fn() {}
+    /// ```
+    /// Use instead:
+    /// ```no_run
+    /// /// This is a footnote[^1].
+    /// ///
+    /// /// [^1]: defined here
+    /// fn my_fn() {}
+    /// ```
+    #[clippy::version = "1.88.0"]
+    pub DOC_SUSPICIOUS_FOOTNOTES,
+    suspicious,
+    "looks like a link or footnote ref, but with no definition"
+}
+
 pub struct Documentation {
     valid_idents: FxHashSet<String>,
     check_private_items: bool,
@@ -638,7 +670,8 @@ impl_lint_pass!(Documentation => [
     DOC_OVERINDENTED_LIST_ITEMS,
     TOO_LONG_FIRST_DOC_PARAGRAPH,
     DOC_INCLUDE_WITHOUT_CFG,
-    DOC_COMMENT_DOUBLE_SPACE_LINEBREAKS
+    DOC_COMMENT_DOUBLE_SPACE_LINEBREAKS,
+    DOC_SUSPICIOUS_FOOTNOTES,
 ]);
 
 impl EarlyLintPass for Documentation {
@@ -825,6 +858,7 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs: &[
             doc: &doc,
             fragments: &fragments,
         },
+        attrs,
     ))
 }
 
@@ -905,6 +939,7 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
     events: Events,
     doc: &str,
     fragments: Fragments<'_>,
+    attrs: &[Attribute],
 ) -> DocHeaders {
     // true if a safety header was found
     let mut headers = DocHeaders::default();
@@ -1148,7 +1183,8 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
                         // Don't check the text associated with external URLs
                         continue;
                     }
-                    text_to_check.push((text, range, code_level));
+                    text_to_check.push((text, range.clone(), code_level));
+                    doc_suspicious_footnotes::check(cx, doc, range, &fragments, attrs);
                 }
             }
             FootnoteReference(_) => {}
diff --git a/src/tools/clippy/clippy_lints/src/endian_bytes.rs b/src/tools/clippy/clippy_lints/src/endian_bytes.rs
index a7670ffce88..75db923b1c3 100644
--- a/src/tools/clippy/clippy_lints/src/endian_bytes.rs
+++ b/src/tools/clippy/clippy_lints/src/endian_bytes.rs
@@ -1,6 +1,6 @@
 use crate::Lint;
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::is_lint_allowed;
+use clippy_utils::{is_lint_allowed, sym};
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::ty::Ty;
@@ -65,9 +65,9 @@ declare_clippy_lint! {
 
 declare_lint_pass!(EndianBytes => [HOST_ENDIAN_BYTES, LITTLE_ENDIAN_BYTES, BIG_ENDIAN_BYTES]);
 
-const HOST_NAMES: [&str; 2] = ["from_ne_bytes", "to_ne_bytes"];
-const LITTLE_NAMES: [&str; 2] = ["from_le_bytes", "to_le_bytes"];
-const BIG_NAMES: [&str; 2] = ["from_be_bytes", "to_be_bytes"];
+const HOST_NAMES: [Symbol; 2] = [sym::from_ne_bytes, sym::to_ne_bytes];
+const LITTLE_NAMES: [Symbol; 2] = [sym::from_le_bytes, sym::to_le_bytes];
+const BIG_NAMES: [Symbol; 2] = [sym::from_be_bytes, sym::to_be_bytes];
 
 #[derive(Clone, Debug)]
 enum LintKind {
@@ -95,7 +95,7 @@ impl LintKind {
         }
     }
 
-    fn as_name(&self, prefix: Prefix) -> &str {
+    fn as_name(&self, prefix: Prefix) -> Symbol {
         let index = usize::from(prefix == Prefix::To);
 
         match self {
@@ -133,7 +133,7 @@ fn maybe_lint_endian_bytes(cx: &LateContext<'_>, expr: &Expr<'_>, prefix: Prefix
     let le = LintKind::Little.as_name(prefix);
     let be = LintKind::Big.as_name(prefix);
 
-    let (lint, other_lints) = match name.as_str() {
+    let (lint, other_lints) = match name {
         name if name == ne => ((&LintKind::Host), [(&LintKind::Little), (&LintKind::Big)]),
         name if name == le => ((&LintKind::Little), [(&LintKind::Host), (&LintKind::Big)]),
         name if name == be => ((&LintKind::Big), [(&LintKind::Host), (&LintKind::Little)]),
diff --git a/src/tools/clippy/clippy_lints/src/format_args.rs b/src/tools/clippy/clippy_lints/src/format_args.rs
index a26e736c7ae..0c39aae9ca9 100644
--- a/src/tools/clippy/clippy_lints/src/format_args.rs
+++ b/src/tools/clippy/clippy_lints/src/format_args.rs
@@ -1,6 +1,8 @@
+use std::collections::hash_map::Entry;
+
 use arrayvec::ArrayVec;
 use clippy_config::Conf;
-use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::macros::{
     FormatArgsStorage, FormatParamUsage, MacroCall, find_format_arg_expr, format_arg_removal_span,
     format_placeholder_format_span, is_assert_macro, is_format_macro, is_panic, matching_root_macro_call,
@@ -9,7 +11,7 @@ use clippy_utils::macros::{
 use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::{SpanRangeExt, snippet};
 use clippy_utils::ty::{implements_trait, is_type_lang_item};
-use clippy_utils::{is_diag_trait_item, is_from_proc_macro, is_in_test};
+use clippy_utils::{is_diag_trait_item, is_from_proc_macro, is_in_test, trait_ref_of_method};
 use itertools::Itertools;
 use rustc_ast::{
     FormatArgPosition, FormatArgPositionKind, FormatArgsPiece, FormatArgumentKind, FormatCount, FormatOptions,
@@ -22,10 +24,12 @@ use rustc_errors::SuggestionStyle::{CompletelyHidden, ShowCode};
 use rustc_hir::{Expr, ExprKind, LangItem};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::ty::adjustment::{Adjust, Adjustment};
-use rustc_middle::ty::{List, Ty, TyCtxt};
+use rustc_middle::ty::{self, GenericArg, List, TraitRef, Ty, TyCtxt, Upcast};
 use rustc_session::impl_lint_pass;
 use rustc_span::edition::Edition::Edition2021;
 use rustc_span::{Span, Symbol, sym};
+use rustc_trait_selection::infer::TyCtxtInferExt;
+use rustc_trait_selection::traits::{Obligation, ObligationCause, Selection, SelectionContext};
 
 declare_clippy_lint! {
     /// ### What it does
@@ -194,12 +198,41 @@ declare_clippy_lint! {
     "use of a format specifier that has no effect"
 }
 
+declare_clippy_lint! {
+    /// ### What it does
+    /// Detects [pointer format] as well as `Debug` formatting of raw pointers or function pointers
+    /// or any types that have a derived `Debug` impl that recursively contains them.
+    ///
+    /// ### Why restrict this?
+    /// The addresses are only useful in very specific contexts, and certain projects may want to keep addresses of
+    /// certain data structures or functions from prying hacker eyes as an additional line of security.
+    ///
+    /// ### Known problems
+    /// The lint currently only looks through derived `Debug` implementations. Checking whether a manual
+    /// implementation prints an address is left as an exercise to the next lint implementer.
+    ///
+    /// ### Example
+    /// ```no_run
+    /// let foo = &0_u32;
+    /// fn bar() {}
+    /// println!("{:p}", foo);
+    /// let _ = format!("{:?}", &(bar as fn()));
+    /// ```
+    ///
+    /// [pointer format]: https://doc.rust-lang.org/std/fmt/index.html#formatting-traits
+    #[clippy::version = "1.88.0"]
+    pub POINTER_FORMAT,
+    restriction,
+    "formatting a pointer"
+}
+
 impl_lint_pass!(FormatArgs<'_> => [
     FORMAT_IN_FORMAT_ARGS,
     TO_STRING_IN_FORMAT_ARGS,
     UNINLINED_FORMAT_ARGS,
     UNNECESSARY_DEBUG_FORMATTING,
     UNUSED_FORMAT_SPECS,
+    POINTER_FORMAT,
 ]);
 
 #[allow(clippy::struct_field_names)]
@@ -208,6 +241,8 @@ pub struct FormatArgs<'tcx> {
     msrv: Msrv,
     ignore_mixed: bool,
     ty_msrv_map: FxHashMap<Ty<'tcx>, Option<RustcVersion>>,
+    has_derived_debug: FxHashMap<Ty<'tcx>, bool>,
+    has_pointer_format: FxHashMap<Ty<'tcx>, bool>,
 }
 
 impl<'tcx> FormatArgs<'tcx> {
@@ -218,6 +253,8 @@ impl<'tcx> FormatArgs<'tcx> {
             msrv: conf.msrv,
             ignore_mixed: conf.allow_mixed_uninlined_format_args,
             ty_msrv_map,
+            has_derived_debug: FxHashMap::default(),
+            has_pointer_format: FxHashMap::default(),
         }
     }
 }
@@ -228,7 +265,7 @@ impl<'tcx> LateLintPass<'tcx> for FormatArgs<'tcx> {
             && is_format_macro(cx, macro_call.def_id)
             && let Some(format_args) = self.format_args.get(cx, expr, macro_call.expn)
         {
-            let linter = FormatArgsExpr {
+            let mut linter = FormatArgsExpr {
                 cx,
                 expr,
                 macro_call: &macro_call,
@@ -236,6 +273,8 @@ impl<'tcx> LateLintPass<'tcx> for FormatArgs<'tcx> {
                 ignore_mixed: self.ignore_mixed,
                 msrv: &self.msrv,
                 ty_msrv_map: &self.ty_msrv_map,
+                has_derived_debug: &mut self.has_derived_debug,
+                has_pointer_format: &mut self.has_pointer_format,
             };
 
             linter.check_templates();
@@ -255,10 +294,12 @@ struct FormatArgsExpr<'a, 'tcx> {
     ignore_mixed: bool,
     msrv: &'a Msrv,
     ty_msrv_map: &'a FxHashMap<Ty<'tcx>, Option<RustcVersion>>,
+    has_derived_debug: &'a mut FxHashMap<Ty<'tcx>, bool>,
+    has_pointer_format: &'a mut FxHashMap<Ty<'tcx>, bool>,
 }
 
 impl<'tcx> FormatArgsExpr<'_, 'tcx> {
-    fn check_templates(&self) {
+    fn check_templates(&mut self) {
         for piece in &self.format_args.template {
             if let FormatArgsPiece::Placeholder(placeholder) = piece
                 && let Ok(index) = placeholder.argument.index
@@ -279,6 +320,17 @@ impl<'tcx> FormatArgsExpr<'_, 'tcx> {
                 if placeholder.format_trait == FormatTrait::Debug {
                     let name = self.cx.tcx.item_name(self.macro_call.def_id);
                     self.check_unnecessary_debug_formatting(name, arg_expr);
+                    if let Some(span) = placeholder.span
+                        && self.has_pointer_debug(self.cx.typeck_results().expr_ty(arg_expr), 0)
+                    {
+                        span_lint(self.cx, POINTER_FORMAT, span, "pointer formatting detected");
+                    }
+                }
+
+                if placeholder.format_trait == FormatTrait::Pointer
+                    && let Some(span) = placeholder.span
+                {
+                    span_lint(self.cx, POINTER_FORMAT, span, "pointer formatting detected");
                 }
             }
         }
@@ -490,6 +542,16 @@ impl<'tcx> FormatArgsExpr<'_, 'tcx> {
             && let ty = cx.typeck_results().expr_ty(value)
             && self.can_display_format(ty)
         {
+            // If the parent function is a method of `Debug`, we don't want to lint
+            // because it is likely that the user wants to use `Debug` formatting.
+            let parent_fn = cx.tcx.hir_get_parent_item(value.hir_id);
+            if let Some(trait_ref) = trait_ref_of_method(cx, parent_fn)
+                && let Some(trait_def_id) = trait_ref.trait_def_id()
+                && cx.tcx.is_diagnostic_item(sym::Debug, trait_def_id)
+            {
+                return;
+            }
+
             let snippet = snippet(cx.sess(), value.span, "..");
             span_lint_and_then(
                 cx,
@@ -559,6 +621,58 @@ impl<'tcx> FormatArgsExpr<'_, 'tcx> {
 
         false
     }
+
+    fn has_pointer_debug(&mut self, ty: Ty<'tcx>, depth: usize) -> bool {
+        let cx = self.cx;
+        let tcx = cx.tcx;
+        if !tcx.recursion_limit().value_within_limit(depth) {
+            return false;
+        }
+        let depth = depth + 1;
+        let typing_env = cx.typing_env();
+        let ty = tcx.normalize_erasing_regions(typing_env, ty);
+        match ty.kind() {
+            ty::RawPtr(..) | ty::FnPtr(..) | ty::FnDef(..) => true,
+            ty::Ref(_, t, _) | ty::Slice(t) | ty::Array(t, _) => self.has_pointer_debug(*t, depth),
+            ty::Tuple(ts) => ts.iter().any(|t| self.has_pointer_debug(t, depth)),
+            ty::Adt(adt, args) => {
+                match self.has_pointer_format.entry(ty) {
+                    Entry::Occupied(o) => return *o.get(),
+                    Entry::Vacant(v) => v.insert(false),
+                };
+                let derived_debug = if let Some(&known) = self.has_derived_debug.get(&ty) {
+                    known
+                } else {
+                    let Some(trait_id) = tcx.get_diagnostic_item(sym::Debug) else {
+                        return false;
+                    };
+                    let (infcx, param_env) = tcx.infer_ctxt().build_with_typing_env(typing_env);
+                    let trait_ref = TraitRef::new(tcx, trait_id, [GenericArg::from(ty)]);
+                    let obligation = Obligation {
+                        cause: ObligationCause::dummy(),
+                        param_env,
+                        recursion_depth: 0,
+                        predicate: trait_ref.upcast(tcx),
+                    };
+                    let selection = SelectionContext::new(&infcx).select(&obligation);
+                    let derived = if let Ok(Some(Selection::UserDefined(data))) = selection {
+                        tcx.has_attr(data.impl_def_id, sym::automatically_derived)
+                    } else {
+                        false
+                    };
+                    self.has_derived_debug.insert(ty, derived);
+                    derived
+                };
+                let pointer_debug = derived_debug
+                    && adt.all_fields().any(|f| {
+                        self.has_pointer_debug(tcx.normalize_erasing_regions(typing_env, f.ty(tcx, args)), depth)
+                    });
+                self.has_pointer_format.insert(ty, pointer_debug);
+                pointer_debug
+            },
+            _ => false,
+        }
+    }
 }
 
 fn make_ty_msrv_map(tcx: TyCtxt<'_>) -> FxHashMap<Ty<'_>, Option<RustcVersion>> {
diff --git a/src/tools/clippy/clippy_lints/src/functions/mod.rs b/src/tools/clippy/clippy_lints/src/functions/mod.rs
index d0d02a382d1..6051dc9479b 100644
--- a/src/tools/clippy/clippy_lints/src/functions/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/functions/mod.rs
@@ -303,7 +303,7 @@ declare_clippy_lint! {
     /// to the name of the method, when there is a field's whose name matches that of the method.
     ///
     /// ### Why is this bad?
-    /// It is most likely that such a  method is a bug caused by a typo or by copy-pasting.
+    /// It is most likely that such a method is a bug caused by a typo or by copy-pasting.
     ///
     /// ### Example
 
diff --git a/src/tools/clippy/clippy_lints/src/if_let_mutex.rs b/src/tools/clippy/clippy_lints/src/if_let_mutex.rs
index 6444e99ae9c..a99118f90f8 100644
--- a/src/tools/clippy/clippy_lints/src/if_let_mutex.rs
+++ b/src/tools/clippy/clippy_lints/src/if_let_mutex.rs
@@ -1,14 +1,13 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::visitors::for_each_expr_without_closures;
-use clippy_utils::{eq_expr_value, higher};
+use clippy_utils::{eq_expr_value, higher, sym};
 use core::ops::ControlFlow;
 use rustc_errors::Diag;
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
 use rustc_span::edition::Edition::Edition2024;
-use rustc_span::sym;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -94,7 +93,7 @@ fn mutex_lock_call<'tcx>(
     op_mutex: Option<&'tcx Expr<'_>>,
 ) -> ControlFlow<&'tcx Expr<'tcx>> {
     if let ExprKind::MethodCall(path, self_arg, [], _) = &expr.kind
-        && path.ident.as_str() == "lock"
+        && path.ident.name == sym::lock
         && let ty = cx.typeck_results().expr_ty(self_arg).peel_refs()
         && is_type_diagnostic_item(cx, ty, sym::Mutex)
         && op_mutex.is_none_or(|op| eq_expr_value(cx, self_arg, op))
diff --git a/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs b/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs
index 0823ef53ef9..c743501da25 100644
--- a/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs
+++ b/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs
@@ -3,7 +3,7 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::sugg::{Sugg, make_binop};
 use clippy_utils::{
-    SpanlessEq, eq_expr_value, higher, is_in_const_context, is_integer_literal, peel_blocks, peel_blocks_with_stmt,
+    SpanlessEq, eq_expr_value, higher, is_in_const_context, is_integer_literal, peel_blocks, peel_blocks_with_stmt, sym,
 };
 use rustc_ast::ast::LitKind;
 use rustc_data_structures::packed::Pu128;
@@ -11,7 +11,7 @@ use rustc_errors::Applicability;
 use rustc_hir::{AssignOpKind, BinOp, BinOpKind, Expr, ExprKind, QPath};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::impl_lint_pass;
-use rustc_span::Span;
+use rustc_span::{Span, Symbol};
 
 declare_clippy_lint! {
     /// ### What it does
@@ -325,7 +325,7 @@ fn check_with_condition<'tcx>(
         }
 
         // Get the variable name
-        let var_name = ares_path.segments[0].ident.name.as_str();
+        let var_name = ares_path.segments[0].ident.name;
         match cond_num_val.kind {
             ExprKind::Lit(cond_lit) => {
                 // Check if the constant is zero
@@ -337,7 +337,7 @@ fn check_with_condition<'tcx>(
                 }
             },
             ExprKind::Path(QPath::TypeRelative(_, name)) => {
-                if name.ident.as_str() == "MIN"
+                if name.ident.name == sym::MIN
                     && let Some(const_id) = cx.typeck_results().type_dependent_def_id(cond_num_val.hir_id)
                     && let Some(impl_id) = cx.tcx.impl_of_method(const_id)
                     && let None = cx.tcx.impl_trait_ref(impl_id) // An inherent impl
@@ -348,7 +348,7 @@ fn check_with_condition<'tcx>(
             },
             ExprKind::Call(func, []) => {
                 if let ExprKind::Path(QPath::TypeRelative(_, name)) = func.kind
-                    && name.ident.as_str() == "min_value"
+                    && name.ident.name == sym::min_value
                     && let Some(func_id) = cx.typeck_results().type_dependent_def_id(func.hir_id)
                     && let Some(impl_id) = cx.tcx.impl_of_method(func_id)
                     && let None = cx.tcx.impl_trait_ref(impl_id) // An inherent impl
@@ -383,7 +383,7 @@ fn subtracts_one<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<&'a Exp
     }
 }
 
-fn print_lint_and_sugg(cx: &LateContext<'_>, var_name: &str, expr: &Expr<'_>) {
+fn print_lint_and_sugg(cx: &LateContext<'_>, var_name: Symbol, expr: &Expr<'_>) {
     span_lint_and_sugg(
         cx,
         IMPLICIT_SATURATING_SUB,
diff --git a/src/tools/clippy/clippy_lints/src/infallible_try_from.rs b/src/tools/clippy/clippy_lints/src/infallible_try_from.rs
new file mode 100644
index 00000000000..b54c289fa7e
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/infallible_try_from.rs
@@ -0,0 +1,76 @@
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::sym;
+use rustc_errors::MultiSpan;
+use rustc_hir::{AssocItemKind, Item, ItemKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::declare_lint_pass;
+
+declare_clippy_lint! {
+    /// ### What it does
+    ///
+    /// Finds manual impls of `TryFrom` with infallible error types.
+    ///
+    /// ### Why is this bad?
+    ///
+    /// Infalliable conversions should be implemented via `From` with the blanket conversion.
+    ///
+    /// ### Example
+    /// ```no_run
+    /// use std::convert::Infallible;
+    /// struct MyStruct(i16);
+    /// impl TryFrom<i16> for MyStruct {
+    ///     type Error = Infallible;
+    ///     fn try_from(other: i16) -> Result<Self, Infallible> {
+    ///         Ok(Self(other.into()))
+    ///     }
+    /// }
+    /// ```
+    /// Use instead:
+    /// ```no_run
+    /// struct MyStruct(i16);
+    /// impl From<i16> for MyStruct {
+    ///     fn from(other: i16) -> Self {
+    ///         Self(other)
+    ///     }
+    /// }
+    /// ```
+    #[clippy::version = "1.88.0"]
+    pub INFALLIBLE_TRY_FROM,
+    suspicious,
+    "TryFrom with infallible Error type"
+}
+declare_lint_pass!(InfallibleTryFrom => [INFALLIBLE_TRY_FROM]);
+
+impl<'tcx> LateLintPass<'tcx> for InfallibleTryFrom {
+    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
+        let ItemKind::Impl(imp) = item.kind else { return };
+        let Some(r#trait) = imp.of_trait else { return };
+        let Some(trait_def_id) = r#trait.trait_def_id() else {
+            return;
+        };
+        if !cx.tcx.is_diagnostic_item(sym::TryFrom, trait_def_id) {
+            return;
+        }
+        for ii in imp.items {
+            if ii.kind == AssocItemKind::Type {
+                let ii = cx.tcx.hir_impl_item(ii.id);
+                if ii.ident.name != sym::Error {
+                    continue;
+                }
+                let ii_ty = ii.expect_type();
+                let ii_ty_span = ii_ty.span;
+                let ii_ty = clippy_utils::ty::ty_from_hir_ty(cx, ii_ty);
+                if !ii_ty.is_inhabited_from(cx.tcx, ii.owner_id.to_def_id(), cx.typing_env()) {
+                    let mut span = MultiSpan::from_span(cx.tcx.def_span(item.owner_id.to_def_id()));
+                    span.push_span_label(ii_ty_span, "infallible error type");
+                    span_lint(
+                        cx,
+                        INFALLIBLE_TRY_FROM,
+                        span,
+                        "infallible TryFrom impl; consider implementing From, instead",
+                    );
+                }
+            }
+        }
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs
index 92eb3d7a7c5..be9142b17fe 100644
--- a/src/tools/clippy/clippy_lints/src/lib.rs
+++ b/src/tools/clippy/clippy_lints/src/lib.rs
@@ -55,6 +55,7 @@ extern crate rustc_session;
 extern crate rustc_span;
 extern crate rustc_target;
 extern crate rustc_trait_selection;
+extern crate smallvec;
 extern crate thin_vec;
 
 #[macro_use]
@@ -65,11 +66,10 @@ extern crate clippy_utils;
 
 mod utils;
 
-pub mod ctfe; // Very important lint, do not remove (rust#125116)
 pub mod declared_lints;
 pub mod deprecated_lints;
 
-// begin lints modules, do not remove this comment, it’s used in `update_lints`
+// begin lints modules, do not remove this comment, it's used in `update_lints`
 mod absolute_paths;
 mod almost_complete_range;
 mod approx_const;
@@ -95,6 +95,7 @@ mod casts;
 mod cfg_not_test;
 mod checked_conversions;
 mod cloned_ref_to_slice_refs;
+mod coerce_container_to_any;
 mod cognitive_complexity;
 mod collapsible_if;
 mod collection_is_never_read;
@@ -169,6 +170,7 @@ mod inconsistent_struct_constructor;
 mod index_refutable_slice;
 mod indexing_slicing;
 mod ineffective_open_options;
+mod infallible_try_from;
 mod infinite_iter;
 mod inherent_impl;
 mod inherent_to_string;
@@ -404,7 +406,7 @@ mod zero_div_zero;
 mod zero_repeat_side_effects;
 mod zero_sized_map_values;
 mod zombie_processes;
-// end lints modules, do not remove this comment, it’s used in `update_lints`
+// end lints modules, do not remove this comment, it's used in `update_lints`
 
 use clippy_config::{Conf, get_configuration_metadata, sanitize_explanation};
 use clippy_utils::macros::FormatArgsStorage;
@@ -583,8 +585,6 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
     let attrs = attr_storage.clone();
     store.register_early_pass(move || Box::new(AttrCollector::new(attrs.clone())));
 
-    store.register_late_pass(|_| Box::new(ctfe::ClippyCtfe));
-
     store.register_late_pass(move |_| Box::new(operators::arithmetic_side_effects::ArithmeticSideEffects::new(conf)));
     store.register_late_pass(|_| Box::new(utils::dump_hir::DumpHir));
     store.register_late_pass(|_| Box::new(utils::author::Author));
@@ -946,5 +946,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
     store.register_late_pass(|_| Box::new(single_option_map::SingleOptionMap));
     store.register_late_pass(move |_| Box::new(redundant_test_prefix::RedundantTestPrefix));
     store.register_late_pass(|_| Box::new(cloned_ref_to_slice_refs::ClonedRefToSliceRefs::new(conf)));
+    store.register_late_pass(|_| Box::new(infallible_try_from::InfallibleTryFrom));
+    store.register_late_pass(|_| Box::new(coerce_container_to_any::CoerceContainerToAny));
     // add lints here, do not remove this comment, it's used in `new_lint`
 }
diff --git a/src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs b/src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs
index 81f14b7b2b0..ddb8bb536c0 100644
--- a/src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs
@@ -2,8 +2,9 @@ use super::MANUAL_FLATTEN;
 use super::utils::make_iterator_snippet;
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::msrvs::{self, Msrv};
+use clippy_utils::source::{HasSession, indent_of, reindent_multiline, snippet_with_applicability};
 use clippy_utils::visitors::is_local_used;
-use clippy_utils::{higher, is_refutable, path_to_local_id, peel_blocks_with_stmt};
+use clippy_utils::{higher, is_refutable, path_to_local_id, peel_blocks_with_stmt, span_contains_comment};
 use rustc_errors::Applicability;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::{Expr, Pat, PatKind};
@@ -39,13 +40,20 @@ pub(super) fn check<'tcx>(
         && msrv.meets(cx, msrvs::ITER_FLATTEN)
         && !is_refutable(cx, inner_pat)
     {
+        if arg.span.from_expansion() || if_then.span.from_expansion() {
+            return;
+        }
         let if_let_type = if some_ctor { "Some" } else { "Ok" };
         // Prepare the error message
         let msg =
             format!("unnecessary `if let` since only the `{if_let_type}` variant of the iterator element is used");
 
         // Prepare the help message
-        let mut applicability = Applicability::MaybeIncorrect;
+        let mut applicability = if span_contains_comment(cx.sess().source_map(), body.span) {
+            Applicability::MaybeIncorrect
+        } else {
+            Applicability::MachineApplicable
+        };
         let arg_snippet = make_iterator_snippet(cx, arg, &mut applicability);
         let copied = match cx.typeck_results().expr_ty(let_expr).kind() {
             ty::Ref(_, inner, _) => match inner.kind() {
@@ -55,20 +63,26 @@ pub(super) fn check<'tcx>(
             _ => "",
         };
 
-        let sugg = format!("{arg_snippet}{copied}.flatten()");
+        let help_msg = "try `.flatten()` and remove the `if let` statement in the for loop";
 
-        // If suggestion is not a one-liner, it won't be shown inline within the error message. In that
-        // case, it will be shown in the extra `help` message at the end, which is why the first
-        // `help_msg` needs to refer to the correct relative position of the suggestion.
-        let help_msg = if sugg.contains('\n') {
-            "remove the `if let` statement in the for loop and then..."
-        } else {
-            "...and remove the `if let` statement in the for loop"
-        };
+        let pat_snippet =
+            snippet_with_applicability(cx, inner_pat.span.source_callsite(), "_", &mut applicability).to_string();
+        let body_snippet =
+            snippet_with_applicability(cx, if_then.span.source_callsite(), "[body]", &mut applicability).to_string();
+        let suggestions = vec![
+            // flatten the iterator
+            (arg.span, format!("{arg_snippet}{copied}.flatten()")),
+            (pat.span, pat_snippet),
+            // remove the `if let` statement
+            (
+                body.span,
+                reindent_multiline(&body_snippet, true, indent_of(cx, body.span)),
+            ),
+        ];
 
         span_lint_and_then(cx, MANUAL_FLATTEN, span, msg, |diag| {
-            diag.span_suggestion(arg.span, "try", sugg, applicability);
             diag.span_help(inner_expr.span, help_msg);
+            diag.multipart_suggestion("try", suggestions, applicability);
         });
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/manual_clamp.rs b/src/tools/clippy/clippy_lints/src/manual_clamp.rs
index 02afe9f0997..42fe386d2c3 100644
--- a/src/tools/clippy/clippy_lints/src/manual_clamp.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_clamp.rs
@@ -8,7 +8,7 @@ use clippy_utils::ty::implements_trait;
 use clippy_utils::visitors::is_const_evaluatable;
 use clippy_utils::{
     MaybePath, eq_expr_value, is_diag_trait_item, is_in_const_context, is_trait_method, path_res, path_to_local_id,
-    peel_blocks, peel_blocks_with_stmt,
+    peel_blocks, peel_blocks_with_stmt, sym,
 };
 use itertools::Itertools;
 use rustc_errors::{Applicability, Diag};
@@ -18,7 +18,6 @@ use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::Ty;
 use rustc_session::impl_lint_pass;
 use rustc_span::Span;
-use rustc_span::symbol::sym;
 use std::cmp::Ordering;
 use std::ops::Deref;
 
@@ -299,9 +298,9 @@ fn is_max_min_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> O
         && (cx.typeck_results().expr_ty_adjusted(input).is_floating_point() || is_trait_method(cx, receiver, sym::Ord))
     {
         let is_float = cx.typeck_results().expr_ty_adjusted(input).is_floating_point();
-        let (min, max) = match (seg_first.ident.as_str(), seg_second.ident.as_str()) {
-            ("min", "max") => (arg_second, arg_first),
-            ("max", "min") => (arg_first, arg_second),
+        let (min, max) = match (seg_first.ident.name, seg_second.ident.name) {
+            (sym::min, sym::max) => (arg_second, arg_first),
+            (sym::max, sym::min) => (arg_first, arg_second),
             _ => return None,
         };
         Some(ClampSuggestion {
diff --git a/src/tools/clippy/clippy_lints/src/manual_string_new.rs b/src/tools/clippy/clippy_lints/src/manual_string_new.rs
index 73ee1c3c78a..9b590e092d0 100644
--- a/src/tools/clippy/clippy_lints/src/manual_string_new.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_string_new.rs
@@ -1,11 +1,12 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::sym;
 use rustc_ast::LitKind;
 use rustc_errors::Applicability::MachineApplicable;
 use rustc_hir::{Expr, ExprKind, PathSegment, QPath, TyKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty;
 use rustc_session::declare_lint_pass;
-use rustc_span::{Span, sym};
+use rustc_span::Span;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -89,9 +90,10 @@ fn warn_then_suggest(cx: &LateContext<'_>, span: Span) {
 
 /// Tries to parse an expression as a method call, emitting the warning if necessary.
 fn parse_method_call(cx: &LateContext<'_>, span: Span, path_segment: &PathSegment<'_>, receiver: &Expr<'_>) {
-    let ident = path_segment.ident.as_str();
     let method_arg_kind = &receiver.kind;
-    if ["to_string", "to_owned", "into"].contains(&ident) && is_expr_kind_empty_str(method_arg_kind) {
+    if matches!(path_segment.ident.name, sym::to_string | sym::to_owned | sym::into)
+        && is_expr_kind_empty_str(method_arg_kind)
+    {
         warn_then_suggest(cx, span);
     } else if let ExprKind::Call(func, [arg]) = method_arg_kind {
         // If our first argument is a function call itself, it could be an `unwrap`-like function.
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 2a5fc8b6609..e0cb5d14d3c 100644
--- a/src/tools/clippy/clippy_lints/src/match_result_ok.rs
+++ b/src/tools/clippy/clippy_lints/src/match_result_ok.rs
@@ -1,12 +1,11 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet_with_context;
 use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{higher, is_res_lang_ctor};
+use clippy_utils::{higher, is_res_lang_ctor, sym};
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind, LangItem, PatKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
-use rustc_span::sym;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -57,7 +56,7 @@ impl<'tcx> LateLintPass<'tcx> for MatchResultOk {
 
         if let ExprKind::MethodCall(ok_path, recv, [], ..) = let_expr.kind //check is expr.ok() has type Result<T,E>.ok(, _)
             && let PatKind::TupleStruct(ref pat_path, [ok_pat], _)  = let_pat.kind //get operation
-            && ok_path.ident.as_str() == "ok"
+            && ok_path.ident.name == sym::ok
             && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result)
             && is_res_lang_ctor(cx, cx.qpath_res(pat_path, let_pat.hir_id), LangItem::OptionSome)
             && let ctxt = expr.span.ctxt()
diff --git a/src/tools/clippy/clippy_lints/src/matches/match_single_binding.rs b/src/tools/clippy/clippy_lints/src/matches/match_single_binding.rs
index adda3586990..6a76c6cedd0 100644
--- a/src/tools/clippy/clippy_lints/src/matches/match_single_binding.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/match_single_binding.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::macros::HirNode;
 use clippy_utils::source::{indent_of, snippet, snippet_block_with_context, snippet_with_context};
-use clippy_utils::{get_parent_expr, is_refutable, peel_blocks};
+use clippy_utils::{is_refutable, peel_blocks};
 use rustc_errors::Applicability;
 use rustc_hir::{Arm, Expr, ExprKind, Node, PatKind, StmtKind};
 use rustc_lint::LateContext;
@@ -9,6 +9,7 @@ use rustc_span::Span;
 
 use super::MATCH_SINGLE_BINDING;
 
+#[derive(Debug)]
 enum AssignmentExpr {
     Assign { span: Span, match_span: Span },
     Local { span: Span, pat_span: Span },
@@ -160,6 +161,17 @@ fn opt_parent_assign_span<'a>(cx: &LateContext<'a>, ex: &Expr<'a>) -> Option<Ass
     None
 }
 
+fn expr_parent_requires_curlies<'a>(cx: &LateContext<'a>, match_expr: &Expr<'a>) -> bool {
+    let parent = cx.tcx.parent_hir_node(match_expr.hir_id);
+    matches!(
+        parent,
+        Node::Expr(Expr {
+            kind: ExprKind::Closure { .. },
+            ..
+        }) | Node::AnonConst(..)
+    )
+}
+
 fn sugg_with_curlies<'a>(
     cx: &LateContext<'a>,
     (ex, match_expr): (&Expr<'a>, &Expr<'a>),
@@ -172,9 +184,7 @@ fn sugg_with_curlies<'a>(
     let mut indent = " ".repeat(indent_of(cx, ex.span).unwrap_or(0));
 
     let (mut cbrace_start, mut cbrace_end) = (String::new(), String::new());
-    if let Some(parent_expr) = get_parent_expr(cx, match_expr)
-        && let ExprKind::Closure { .. } = parent_expr.kind
-    {
+    if expr_parent_requires_curlies(cx, match_expr) {
         cbrace_end = format!("\n{indent}}}");
         // Fix body indent due to the closure
         indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0));
diff --git a/src/tools/clippy/clippy_lints/src/matches/match_str_case_mismatch.rs b/src/tools/clippy/clippy_lints/src/matches/match_str_case_mismatch.rs
index 65b93a095b9..8b4c1700051 100644
--- a/src/tools/clippy/clippy_lints/src/matches/match_str_case_mismatch.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/match_str_case_mismatch.rs
@@ -1,6 +1,7 @@
 use std::ops::ControlFlow;
 
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::sym;
 use clippy_utils::ty::is_type_lang_item;
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
@@ -42,7 +43,7 @@ impl<'tcx> Visitor<'tcx> for MatchExprVisitor<'_, 'tcx> {
     type Result = ControlFlow<CaseMethod>;
     fn visit_expr(&mut self, ex: &'tcx Expr<'_>) -> Self::Result {
         if let ExprKind::MethodCall(segment, receiver, [], _) = ex.kind {
-            let result = self.case_altered(segment.ident.as_str(), receiver);
+            let result = self.case_altered(segment.ident.name, receiver);
             if result.is_break() {
                 return result;
             }
@@ -53,7 +54,7 @@ impl<'tcx> Visitor<'tcx> for MatchExprVisitor<'_, 'tcx> {
 }
 
 impl MatchExprVisitor<'_, '_> {
-    fn case_altered(&mut self, segment_ident: &str, receiver: &Expr<'_>) -> ControlFlow<CaseMethod> {
+    fn case_altered(&mut self, segment_ident: Symbol, receiver: &Expr<'_>) -> ControlFlow<CaseMethod> {
         if let Some(case_method) = get_case_method(segment_ident) {
             let ty = self.cx.typeck_results().expr_ty(receiver).peel_refs();
 
@@ -66,12 +67,12 @@ impl MatchExprVisitor<'_, '_> {
     }
 }
 
-fn get_case_method(segment_ident_str: &str) -> Option<CaseMethod> {
-    match segment_ident_str {
-        "to_lowercase" => Some(CaseMethod::LowerCase),
-        "to_ascii_lowercase" => Some(CaseMethod::AsciiLowerCase),
-        "to_uppercase" => Some(CaseMethod::UpperCase),
-        "to_ascii_uppercase" => Some(CaseMethod::AsciiUppercase),
+fn get_case_method(segment_ident: Symbol) -> Option<CaseMethod> {
+    match segment_ident {
+        sym::to_lowercase => Some(CaseMethod::LowerCase),
+        sym::to_ascii_lowercase => Some(CaseMethod::AsciiLowerCase),
+        sym::to_uppercase => Some(CaseMethod::UpperCase),
+        sym::to_ascii_uppercase => Some(CaseMethod::AsciiUppercase),
         _ => None,
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/methods/ip_constant.rs b/src/tools/clippy/clippy_lints/src/methods/ip_constant.rs
new file mode 100644
index 00000000000..83803fba6a1
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/methods/ip_constant.rs
@@ -0,0 +1,52 @@
+use clippy_utils::consts::{ConstEvalCtxt, Constant};
+use clippy_utils::diagnostics::span_lint_and_then;
+use rustc_errors::Applicability;
+use rustc_hir::{Expr, ExprKind, QPath, Ty, TyKind};
+use rustc_lint::LateContext;
+use rustc_span::sym;
+use smallvec::SmallVec;
+
+use super::IP_CONSTANT;
+
+pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, func: &Expr<'_>, args: &[Expr<'_>]) {
+    if let ExprKind::Path(QPath::TypeRelative(
+        Ty {
+            kind: TyKind::Path(QPath::Resolved(_, func_path)),
+            ..
+        },
+        p,
+    )) = func.kind
+        && p.ident.name == sym::new
+        && let Some(func_def_id) = func_path.res.opt_def_id()
+        && matches!(
+            cx.tcx.get_diagnostic_name(func_def_id),
+            Some(sym::Ipv4Addr | sym::Ipv6Addr)
+        )
+        && let Some(args) = args
+            .iter()
+            .map(|arg| {
+                if let Some(Constant::Int(constant @ (0 | 1 | 127 | 255))) = ConstEvalCtxt::new(cx).eval(arg) {
+                    u8::try_from(constant).ok()
+                } else {
+                    None
+                }
+            })
+            .collect::<Option<SmallVec<[u8; 8]>>>()
+    {
+        let constant_name = match args.as_slice() {
+            [0, 0, 0, 0] | [0, 0, 0, 0, 0, 0, 0, 0] => "UNSPECIFIED",
+            [127, 0, 0, 1] | [0, 0, 0, 0, 0, 0, 0, 1] => "LOCALHOST",
+            [255, 255, 255, 255] => "BROADCAST",
+            _ => return,
+        };
+
+        span_lint_and_then(cx, IP_CONSTANT, expr.span, "hand-coded well-known IP address", |diag| {
+            diag.span_suggestion_verbose(
+                expr.span.with_lo(p.ident.span.lo()),
+                "use",
+                constant_name,
+                Applicability::MachineApplicable,
+            );
+        });
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs b/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs
index c88462129af..cbb1b450e60 100644
--- a/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs
@@ -46,7 +46,7 @@ pub(super) fn check<'tcx>(
 
         if let ExprKind::Path(rustc_hir::QPath::Resolved(_, path)) = body_expr.kind
             && let [local_ident] = path.segments
-            && local_ident.ident.as_str() == bound_ident.as_str()
+            && local_ident.ident.name == bound_ident.name
         {
             span_lint_and_sugg(
                 cx,
diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_saturating_arithmetic.rs b/src/tools/clippy/clippy_lints/src/methods/manual_saturating_arithmetic.rs
index e2df8ce1513..c785b23bc9c 100644
--- a/src/tools/clippy/clippy_lints/src/methods/manual_saturating_arithmetic.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/manual_saturating_arithmetic.rs
@@ -72,9 +72,9 @@ fn is_min_or_max(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<MinMax> {
     if let hir::ExprKind::Call(func, []) = &expr.kind
         && let hir::ExprKind::Path(hir::QPath::TypeRelative(_, segment)) = &func.kind
     {
-        match segment.ident.as_str() {
-            "max_value" => return Some(MinMax::Max),
-            "min_value" => return Some(MinMax::Min),
+        match segment.ident.name {
+            sym::max_value => return Some(MinMax::Max),
+            sym::min_value => return Some(MinMax::Min),
             _ => {},
         }
     }
diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs
index bc159206985..347960e0003 100644
--- a/src/tools/clippy/clippy_lints/src/methods/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs
@@ -37,6 +37,7 @@ mod inefficient_to_string;
 mod inspect_for_each;
 mod into_iter_on_ref;
 mod io_other_error;
+mod ip_constant;
 mod is_digit_ascii_radix;
 mod is_empty;
 mod iter_cloned_collect;
@@ -4528,6 +4529,42 @@ declare_clippy_lint! {
     "detect swap with a temporary value"
 }
 
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for IP addresses that could be replaced with predefined constants such as
+    /// `Ipv4Addr::new(127, 0, 0, 1)` instead of using the appropriate constants.
+    ///
+    /// ### Why is this bad?
+    /// Using specific IP addresses like `127.0.0.1` or `::1` is less clear and less maintainable than using the
+    /// predefined constants `Ipv4Addr::LOCALHOST` or `Ipv6Addr::LOCALHOST`. These constants improve code
+    /// readability, make the intent explicit, and are less error-prone.
+    ///
+    /// ### Example
+    /// ```no_run
+    /// use std::net::{Ipv4Addr, Ipv6Addr};
+    ///
+    /// // IPv4 loopback
+    /// let addr_v4 = Ipv4Addr::new(127, 0, 0, 1);
+    ///
+    /// // IPv6 loopback
+    /// let addr_v6 = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1);
+    /// ```
+    /// Use instead:
+    /// ```no_run
+    /// use std::net::{Ipv4Addr, Ipv6Addr};
+    ///
+    /// // IPv4 loopback
+    /// let addr_v4 = Ipv4Addr::LOCALHOST;
+    ///
+    /// // IPv6 loopback
+    /// let addr_v6 = Ipv6Addr::LOCALHOST;
+    /// ```
+    #[clippy::version = "1.89.0"]
+    pub IP_CONSTANT,
+    pedantic,
+    "hardcoded localhost IP address"
+}
+
 #[expect(clippy::struct_excessive_bools)]
 pub struct Methods {
     avoid_breaking_exported_api: bool,
@@ -4706,6 +4743,7 @@ impl_lint_pass!(Methods => [
     MANUAL_CONTAINS,
     IO_OTHER_ERROR,
     SWAP_WITH_TEMPORARY,
+    IP_CONSTANT,
 ]);
 
 /// Extracts a method call name, args, and `Span` of the method name.
@@ -4738,6 +4776,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
                 useless_nonzero_new_unchecked::check(cx, expr, func, args, self.msrv);
                 io_other_error::check(cx, expr, func, args, self.msrv);
                 swap_with_temporary::check(cx, expr, func, args);
+                ip_constant::check(cx, expr, func, args);
             },
             ExprKind::MethodCall(method_call, receiver, args, _) => {
                 let method_span = method_call.ident.span;
diff --git a/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs b/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs
index 2b75d6a8248..0075bf166cc 100644
--- a/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs
@@ -44,11 +44,10 @@ pub(super) fn check<'tcx>(
 
             if let ExprKind::MethodCall(name, _, args @ ([] | [_]), _) = parent.kind {
                 let mut app = Applicability::MachineApplicable;
-                let name = name.ident.as_str();
                 let collect_ty = cx.typeck_results().expr_ty(collect_expr);
 
-                let sugg: String = match name {
-                    "len" => {
+                let sugg: String = match name.ident.name {
+                    sym::len => {
                         if let Some(adt) = collect_ty.ty_adt_def()
                             && matches!(
                                 cx.tcx.get_diagnostic_name(adt.did()),
@@ -60,13 +59,13 @@ pub(super) fn check<'tcx>(
                             return;
                         }
                     },
-                    "is_empty"
+                    sym::is_empty
                         if is_is_empty_sig(cx, parent.hir_id)
                             && iterates_same_ty(cx, cx.typeck_results().expr_ty(iter_expr), collect_ty) =>
                     {
                         "next().is_none()".into()
                     },
-                    "contains" => {
+                    sym::contains => {
                         if is_contains_sig(cx, parent.hir_id, iter_expr)
                             && let Some(arg) = args.first()
                         {
diff --git a/src/tools/clippy/clippy_lints/src/methods/open_options.rs b/src/tools/clippy/clippy_lints/src/methods/open_options.rs
index bce314e64f0..fd368024177 100644
--- a/src/tools/clippy/clippy_lints/src/methods/open_options.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/open_options.rs
@@ -1,14 +1,14 @@
 use rustc_data_structures::fx::FxHashMap;
 
 use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
-use clippy_utils::paths;
 use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::{paths, sym};
 use rustc_ast::ast::LitKind;
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::LateContext;
 use rustc_middle::ty::Ty;
+use rustc_span::Span;
 use rustc_span::source_map::Spanned;
-use rustc_span::{Span, sym};
 
 use super::{NONSENSICAL_OPEN_OPTIONS, SUSPICIOUS_OPEN_OPTIONS};
 
@@ -87,23 +87,23 @@ fn get_open_options(
                 _ => Argument::Unknown,
             };
 
-            match path.ident.as_str() {
-                "create" => {
+            match path.ident.name {
+                sym::create => {
                     options.push((OpenOption::Create, argument_option, span));
                 },
-                "create_new" => {
+                sym::create_new => {
                     options.push((OpenOption::CreateNew, argument_option, span));
                 },
-                "append" => {
+                sym::append => {
                     options.push((OpenOption::Append, argument_option, span));
                 },
-                "truncate" => {
+                sym::truncate => {
                     options.push((OpenOption::Truncate, argument_option, span));
                 },
-                "read" => {
+                sym::read => {
                     options.push((OpenOption::Read, argument_option, span));
                 },
-                "write" => {
+                sym::write => {
                     options.push((OpenOption::Write, argument_option, span));
                 },
                 _ => {
diff --git a/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs
index 6935ae1f391..6f78d6c6128 100644
--- a/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs
@@ -285,9 +285,9 @@ fn parse_iter_usage<'tcx>(
             let did = cx.typeck_results().type_dependent_def_id(e.hir_id)?;
             let iter_id = cx.tcx.get_diagnostic_item(sym::Iterator)?;
 
-            match (name.ident.as_str(), args) {
-                ("next", []) if cx.tcx.trait_of_item(did) == Some(iter_id) => (IterUsageKind::Nth(0), e.span),
-                ("next_tuple", []) => {
+            match (name.ident.name, args) {
+                (sym::next, []) if cx.tcx.trait_of_item(did) == Some(iter_id) => (IterUsageKind::Nth(0), e.span),
+                (sym::next_tuple, []) => {
                     return if paths::ITERTOOLS_NEXT_TUPLE.matches(cx, did)
                         && let ty::Adt(adt_def, subs) = cx.typeck_results().expr_ty(e).kind()
                         && cx.tcx.is_diagnostic_item(sym::Option, adt_def.did())
@@ -303,7 +303,7 @@ fn parse_iter_usage<'tcx>(
                         None
                     };
                 },
-                ("nth" | "skip", [idx_expr]) if cx.tcx.trait_of_item(did) == Some(iter_id) => {
+                (sym::nth | sym::skip, [idx_expr]) if cx.tcx.trait_of_item(did) == Some(iter_id) => {
                     if let Some(Constant::Int(idx)) = ConstEvalCtxt::new(cx).eval(idx_expr) {
                         let span = if name.ident.as_str() == "nth" {
                             e.span
diff --git a/src/tools/clippy/clippy_lints/src/multiple_bound_locations.rs b/src/tools/clippy/clippy_lints/src/multiple_bound_locations.rs
index 4b32ba83b32..741f38f9756 100644
--- a/src/tools/clippy/clippy_lints/src/multiple_bound_locations.rs
+++ b/src/tools/clippy/clippy_lints/src/multiple_bound_locations.rs
@@ -47,7 +47,7 @@ impl EarlyLintPass for MultipleBoundLocations {
 
             for param in &generics.params {
                 if !param.bounds.is_empty() {
-                    generic_params_with_bounds.insert(param.ident.name.as_str(), param.ident.span);
+                    generic_params_with_bounds.insert(param.ident.as_str(), param.ident.span);
                 }
             }
             for clause in &generics.where_clause.predicates {
@@ -64,7 +64,7 @@ impl EarlyLintPass for MultipleBoundLocations {
                     },
                     WherePredicateKind::RegionPredicate(pred) => {
                         if !pred.bounds.is_empty()
-                            && let Some(bound_span) = generic_params_with_bounds.get(&pred.lifetime.ident.name.as_str())
+                            && let Some(bound_span) = generic_params_with_bounds.get(&pred.lifetime.ident.as_str())
                         {
                             emit_lint(cx, *bound_span, pred.lifetime.ident.span);
                         }
diff --git a/src/tools/clippy/clippy_lints/src/non_canonical_impls.rs b/src/tools/clippy/clippy_lints/src/non_canonical_impls.rs
index 93865197ec9..04b09276966 100644
--- a/src/tools/clippy/clippy_lints/src/non_canonical_impls.rs
+++ b/src/tools/clippy/clippy_lints/src/non_canonical_impls.rs
@@ -293,7 +293,14 @@ fn self_cmp_call<'tcx>(
         ExprKind::Call(path, [_, _]) => path_res(cx, path)
             .opt_def_id()
             .is_some_and(|def_id| cx.tcx.is_diagnostic_item(sym::ord_cmp_method, def_id)),
-        ExprKind::MethodCall(_, _, [_other], ..) => {
+        ExprKind::MethodCall(_, recv, [_], ..) => {
+            let ExprKind::Path(path) = recv.kind else {
+                return false;
+            };
+            if last_path_segment(&path).ident.name != kw::SelfLower {
+                return false;
+            }
+
             // We can set this to true here no matter what as if it's a `MethodCall` and goes to the
             // `else` branch, it must be a method named `cmp` that isn't `Ord::cmp`
             *needs_fully_qualified = true;
diff --git a/src/tools/clippy/clippy_lints/src/pathbuf_init_then_push.rs b/src/tools/clippy/clippy_lints/src/pathbuf_init_then_push.rs
index 35caac855cf..4ce6827cac9 100644
--- a/src/tools/clippy/clippy_lints/src/pathbuf_init_then_push.rs
+++ b/src/tools/clippy/clippy_lints/src/pathbuf_init_then_push.rs
@@ -1,14 +1,14 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::path_to_local_id;
 use clippy_utils::source::{SpanRangeExt, snippet};
 use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::{path_to_local_id, sym};
 use rustc_ast::{LitKind, StrStyle};
 use rustc_errors::Applicability;
 use rustc_hir::def::Res;
 use rustc_hir::{BindingMode, Block, Expr, ExprKind, HirId, LetStmt, PatKind, QPath, Stmt, StmtKind, TyKind};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_session::impl_lint_pass;
-use rustc_span::{Span, Symbol, sym};
+use rustc_span::{Span, Symbol};
 
 declare_clippy_lint! {
     /// ### What it does
@@ -177,7 +177,7 @@ impl<'tcx> LateLintPass<'tcx> for PathbufThenPush<'tcx> {
             && let StmtKind::Expr(expr) | StmtKind::Semi(expr) = stmt.kind
             && let ExprKind::MethodCall(name, self_arg, [arg_expr], _) = expr.kind
             && path_to_local_id(self_arg, searcher.local_id)
-            && name.ident.as_str() == "push"
+            && name.ident.name == sym::push
         {
             searcher.err_span = searcher.err_span.to(stmt.span);
             searcher.arg = Some(*arg_expr);
diff --git a/src/tools/clippy/clippy_lints/src/ptr.rs b/src/tools/clippy/clippy_lints/src/ptr.rs
index 9149406642d..94cdcf00054 100644
--- a/src/tools/clippy/clippy_lints/src/ptr.rs
+++ b/src/tools/clippy/clippy_lints/src/ptr.rs
@@ -2,7 +2,7 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then, span_lin
 use clippy_utils::source::SpanRangeExt;
 use clippy_utils::sugg::Sugg;
 use clippy_utils::visitors::contains_unsafe_block;
-use clippy_utils::{get_expr_use_or_unification_node, is_lint_allowed, path_def_id, path_to_local, std_or_core};
+use clippy_utils::{get_expr_use_or_unification_node, is_lint_allowed, path_def_id, path_to_local, std_or_core, sym};
 use hir::LifetimeKind;
 use rustc_abi::ExternAbi;
 use rustc_errors::{Applicability, MultiSpan};
@@ -18,8 +18,8 @@ use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::hir::nested_filter;
 use rustc_middle::ty::{self, Binder, ClauseKind, ExistentialPredicate, List, PredicateKind, Ty};
 use rustc_session::declare_lint_pass;
+use rustc_span::Span;
 use rustc_span::symbol::Symbol;
-use rustc_span::{Span, sym};
 use rustc_trait_selection::infer::InferCtxtExt as _;
 use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
 use std::{fmt, iter};
@@ -268,6 +268,7 @@ impl<'tcx> LateLintPass<'tcx> for Ptr {
                 (false, false, true) if let Some(sugg) = Sugg::hir_opt(cx, l) => sugg.maybe_paren(),
                 _ => return check_ptr_eq(cx, expr, op.node, l, r),
             };
+            let invert = if op.node == BinOpKind::Eq { "" } else { "!" };
 
             span_lint_and_sugg(
                 cx,
@@ -275,7 +276,7 @@ impl<'tcx> LateLintPass<'tcx> for Ptr {
                 expr.span,
                 "comparing with null is better expressed by the `.is_null()` method",
                 "try",
-                format!("{non_null_path_snippet}.is_null()"),
+                format!("{invert}{non_null_path_snippet}.is_null()",),
                 Applicability::MachineApplicable,
             );
         }
@@ -299,7 +300,7 @@ struct PtrArg<'tcx> {
     emission_id: HirId,
     span: Span,
     ty_name: Symbol,
-    method_renames: &'static [(&'static str, &'static str)],
+    method_renames: &'static [(Symbol, &'static str)],
     ref_prefix: RefPrefix,
     deref_ty: DerefTy<'tcx>,
 }
@@ -385,6 +386,7 @@ impl<'tcx> DerefTy<'tcx> {
     }
 }
 
+#[expect(clippy::too_many_lines)]
 fn check_fn_args<'cx, 'tcx: 'cx>(
     cx: &'cx LateContext<'tcx>,
     fn_sig: ty::FnSig<'tcx>,
@@ -409,7 +411,7 @@ fn check_fn_args<'cx, 'tcx: 'cx>(
                 let emission_id = params.get(i).map_or(hir_ty.hir_id, |param| param.hir_id);
                 let (method_renames, deref_ty) = match cx.tcx.get_diagnostic_name(adt.did()) {
                     Some(sym::Vec) => (
-                        [("clone", ".to_owned()")].as_slice(),
+                        [(sym::clone, ".to_owned()")].as_slice(),
                         DerefTy::Slice(
                             name.args.and_then(|args| args.args.first()).and_then(|arg| {
                                 if let GenericArg::Type(ty) = arg {
@@ -421,10 +423,14 @@ fn check_fn_args<'cx, 'tcx: 'cx>(
                             args.type_at(0),
                         ),
                     ),
-                    _ if Some(adt.did()) == cx.tcx.lang_items().string() => {
-                        ([("clone", ".to_owned()"), ("as_str", "")].as_slice(), DerefTy::Str)
-                    },
-                    Some(sym::PathBuf) => ([("clone", ".to_path_buf()"), ("as_path", "")].as_slice(), DerefTy::Path),
+                    _ if Some(adt.did()) == cx.tcx.lang_items().string() => (
+                        [(sym::clone, ".to_owned()"), (sym::as_str, "")].as_slice(),
+                        DerefTy::Str,
+                    ),
+                    Some(sym::PathBuf) => (
+                        [(sym::clone, ".to_path_buf()"), (sym::as_path, "")].as_slice(),
+                        DerefTy::Path,
+                    ),
                     Some(sym::Cow) if mutability == Mutability::Not => {
                         if let Some((lifetime, ty)) = name.args.and_then(|args| {
                             if let [GenericArg::Lifetime(lifetime), ty] = args.args {
@@ -595,7 +601,7 @@ fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &Body<'tcx>, args: &[
                     if let ExprKind::MethodCall(name, receiver, ..) = use_expr.kind
                         && receiver.hir_id == child_id
                     {
-                        let name = name.ident.as_str();
+                        let name = name.ident.name;
 
                         // Check if the method can be renamed.
                         if let Some((_, replacement)) = args.method_renames.iter().find(|&&(x, _)| x == name) {
diff --git a/src/tools/clippy/clippy_lints/src/read_zero_byte_vec.rs b/src/tools/clippy/clippy_lints/src/read_zero_byte_vec.rs
index 49b522994fb..6b1dc864fb7 100644
--- a/src/tools/clippy/clippy_lints/src/read_zero_byte_vec.rs
+++ b/src/tools/clippy/clippy_lints/src/read_zero_byte_vec.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then};
-use clippy_utils::get_enclosing_block;
 use clippy_utils::higher::{VecInitKind, get_vec_init_kind};
 use clippy_utils::source::snippet;
+use clippy_utils::{get_enclosing_block, sym};
 
 use hir::{Expr, ExprKind, HirId, LetStmt, PatKind, PathSegment, QPath, StmtKind};
 use rustc_errors::Applicability;
@@ -87,7 +87,7 @@ impl<'tcx> LateLintPass<'tcx> for ReadZeroByteVec {
                                 diag.span_suggestion(
                                     expr.span,
                                     "try",
-                                    format!("{}.resize({len}, 0); {}", ident.as_str(), snippet(cx, expr.span, "..")),
+                                    format!("{}.resize({len}, 0); {}", ident, snippet(cx, expr.span, "..")),
                                     applicability,
                                 );
                             },
@@ -106,7 +106,7 @@ impl<'tcx> LateLintPass<'tcx> for ReadZeroByteVec {
                                         "try",
                                         format!(
                                             "{}.resize({}, 0); {}",
-                                            ident.as_str(),
+                                            ident,
                                             snippet(cx, e.span, ".."),
                                             snippet(cx, expr.span, "..")
                                         ),
@@ -142,8 +142,8 @@ impl<'tcx> Visitor<'tcx> for ReadVecVisitor<'tcx> {
         if let ExprKind::MethodCall(path, receiver, args, _) = e.kind {
             let PathSegment { ident, .. } = *path;
 
-            match ident.as_str() {
-                "read" | "read_exact" => {
+            match ident.name {
+                sym::read | sym::read_exact => {
                     let [arg] = args else { return };
                     if let ExprKind::AddrOf(_, hir::Mutability::Mut, inner) = arg.kind
                         && let ExprKind::Path(QPath::Resolved(None, inner_path)) = inner.kind
@@ -155,7 +155,7 @@ impl<'tcx> Visitor<'tcx> for ReadVecVisitor<'tcx> {
                         return;
                     }
                 },
-                "resize" => {
+                sym::resize => {
                     // If the Vec is resized, then it's a valid read
                     if let ExprKind::Path(QPath::Resolved(_, inner_path)) = receiver.kind
                         && let Res::Local(res_id) = inner_path.res
diff --git a/src/tools/clippy/clippy_lints/src/reserve_after_initialization.rs b/src/tools/clippy/clippy_lints/src/reserve_after_initialization.rs
index 152d7450f5f..51adbbcd58b 100644
--- a/src/tools/clippy/clippy_lints/src/reserve_after_initialization.rs
+++ b/src/tools/clippy/clippy_lints/src/reserve_after_initialization.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::higher::{VecInitKind, get_vec_init_kind};
 use clippy_utils::source::snippet;
-use clippy_utils::{is_from_proc_macro, path_to_local_id};
+use clippy_utils::{is_from_proc_macro, path_to_local_id, sym};
 use rustc_errors::Applicability;
 use rustc_hir::def::Res;
 use rustc_hir::{BindingMode, Block, Expr, ExprKind, HirId, LetStmt, PatKind, QPath, Stmt, StmtKind};
@@ -126,7 +126,7 @@ impl<'tcx> LateLintPass<'tcx> for ReserveAfterInitialization {
             if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = stmt.kind
                 && let ExprKind::MethodCall(name, self_arg, [space_hint], _) = expr.kind
                 && path_to_local_id(self_arg, searcher.local_id)
-                && name.ident.as_str() == "reserve"
+                && name.ident.name == sym::reserve
                 && !is_from_proc_macro(cx, expr)
             {
                 self.searcher = Some(VecReserveSearcher {
diff --git a/src/tools/clippy/clippy_lints/src/semicolon_block.rs b/src/tools/clippy/clippy_lints/src/semicolon_block.rs
index d85f4a8fa01..f6c128d4c52 100644
--- a/src/tools/clippy/clippy_lints/src/semicolon_block.rs
+++ b/src/tools/clippy/clippy_lints/src/semicolon_block.rs
@@ -143,7 +143,7 @@ impl LateLintPass<'_> for SemicolonBlock {
             StmtKind::Expr(Expr {
                 kind: ExprKind::Block(block, _),
                 ..
-            }) if !block.span.from_expansion() => {
+            }) if !block.span.from_expansion() && stmt.span.contains(block.span) => {
                 let Block {
                     expr: None,
                     stmts: [.., stmt],
diff --git a/src/tools/clippy/clippy_lints/src/serde_api.rs b/src/tools/clippy/clippy_lints/src/serde_api.rs
index a64b9b22378..b36a5d6d502 100644
--- a/src/tools/clippy/clippy_lints/src/serde_api.rs
+++ b/src/tools/clippy/clippy_lints/src/serde_api.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint;
-use clippy_utils::paths;
+use clippy_utils::{paths, sym};
 use rustc_hir::{Impl, Item, ItemKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
@@ -36,9 +36,9 @@ impl<'tcx> LateLintPass<'tcx> for SerdeApi {
                 let mut seen_str = None;
                 let mut seen_string = None;
                 for item in *items {
-                    match item.ident.as_str() {
-                        "visit_str" => seen_str = Some(item.span),
-                        "visit_string" => seen_string = Some(item.span),
+                    match item.ident.name {
+                        sym::visit_str => seen_str = Some(item.span),
+                        sym::visit_string => seen_string = Some(item.span),
                         _ => {},
                     }
                 }
diff --git a/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs b/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs
index 3d39386ecf9..442b3250d86 100644
--- a/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs
+++ b/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs
@@ -1,13 +1,13 @@
 use clippy_config::Conf;
-use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::is_from_proc_macro;
 use clippy_utils::msrvs::Msrv;
 use rustc_attr_data_structures::{StabilityLevel, StableSince};
 use rustc_errors::Applicability;
 use rustc_hir::def::Res;
 use rustc_hir::def_id::DefId;
-use rustc_hir::{HirId, Path, PathSegment};
-use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_hir::{Block, Body, HirId, Path, PathSegment};
+use rustc_lint::{LateContext, LateLintPass, Lint, LintContext};
 use rustc_session::impl_lint_pass;
 use rustc_span::symbol::kw;
 use rustc_span::{Span, sym};
@@ -88,24 +88,35 @@ declare_clippy_lint! {
 }
 
 pub struct StdReexports {
-    // Paths which can be either a module or a macro (e.g. `std::env`) will cause this check to happen
-    // twice. First for the mod, second for the macro. This is used to avoid the lint reporting for the macro
-    // when the path could be also be used to access the module.
-    prev_span: Span,
+    lint_point: (Span, Option<LintPoint>),
     msrv: Msrv,
 }
 
 impl StdReexports {
     pub fn new(conf: &'static Conf) -> Self {
         Self {
-            prev_span: Span::default(),
+            lint_point: Default::default(),
             msrv: conf.msrv,
         }
     }
+
+    fn lint_if_finish(&mut self, cx: &LateContext<'_>, (span, item): (Span, Option<LintPoint>)) {
+        if span.source_equal(self.lint_point.0) {
+            return;
+        }
+
+        if !self.lint_point.0.is_dummy() {
+            emit_lints(cx, &self.lint_point);
+        }
+
+        self.lint_point = (span, item);
+    }
 }
 
 impl_lint_pass!(StdReexports => [STD_INSTEAD_OF_CORE, STD_INSTEAD_OF_ALLOC, ALLOC_INSTEAD_OF_CORE]);
 
+type LintPoint = (&'static Lint, &'static str, &'static str);
+
 impl<'tcx> LateLintPass<'tcx> for StdReexports {
     fn check_path(&mut self, cx: &LateContext<'tcx>, path: &Path<'tcx>, _: HirId) {
         if let Res::Def(_, def_id) = path.res
@@ -119,7 +130,7 @@ impl<'tcx> LateLintPass<'tcx> for StdReexports {
                     sym::core => (STD_INSTEAD_OF_CORE, "std", "core"),
                     sym::alloc => (STD_INSTEAD_OF_ALLOC, "std", "alloc"),
                     _ => {
-                        self.prev_span = first_segment.ident.span;
+                        self.lint_if_finish(cx, (first_segment.ident.span, None));
                         return;
                     },
                 },
@@ -127,32 +138,44 @@ impl<'tcx> LateLintPass<'tcx> for StdReexports {
                     if cx.tcx.crate_name(def_id.krate) == sym::core {
                         (ALLOC_INSTEAD_OF_CORE, "alloc", "core")
                     } else {
-                        self.prev_span = first_segment.ident.span;
+                        self.lint_if_finish(cx, (first_segment.ident.span, None));
                         return;
                     }
                 },
                 _ => return,
             };
-            if first_segment.ident.span != self.prev_span {
-                #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
-                span_lint_and_then(
-                    cx,
-                    lint,
-                    first_segment.ident.span,
-                    format!("used import from `{used_mod}` instead of `{replace_with}`"),
-                    |diag| {
-                        diag.span_suggestion(
-                            first_segment.ident.span,
-                            format!("consider importing the item from `{replace_with}`"),
-                            replace_with.to_string(),
-                            Applicability::MachineApplicable,
-                        );
-                    },
-                );
-                self.prev_span = first_segment.ident.span;
-            }
+
+            self.lint_if_finish(cx, (first_segment.ident.span, Some((lint, used_mod, replace_with))));
         }
     }
+
+    fn check_block_post(&mut self, cx: &LateContext<'tcx>, _: &Block<'tcx>) {
+        self.lint_if_finish(cx, Default::default());
+    }
+
+    fn check_body_post(&mut self, cx: &LateContext<'tcx>, _: &Body<'tcx>) {
+        self.lint_if_finish(cx, Default::default());
+    }
+
+    fn check_crate_post(&mut self, cx: &LateContext<'tcx>) {
+        self.lint_if_finish(cx, Default::default());
+    }
+}
+
+fn emit_lints(cx: &LateContext<'_>, (span, item): &(Span, Option<LintPoint>)) {
+    let Some((lint, used_mod, replace_with)) = item else {
+        return;
+    };
+
+    span_lint_and_sugg(
+        cx,
+        lint,
+        *span,
+        format!("used import from `{used_mod}` instead of `{replace_with}`"),
+        format!("consider importing the item from `{replace_with}`"),
+        (*replace_with).to_string(),
+        Applicability::MachineApplicable,
+    );
 }
 
 /// Returns the first named segment of a [`Path`].
diff --git a/src/tools/clippy/clippy_lints/src/strings.rs b/src/tools/clippy/clippy_lints/src/strings.rs
index 73a9fe71e00..1cda6f596f4 100644
--- a/src/tools/clippy/clippy_lints/src/strings.rs
+++ b/src/tools/clippy/clippy_lints/src/strings.rs
@@ -560,7 +560,7 @@ impl<'tcx> LateLintPass<'tcx> for TrimSplitWhitespace {
             && let Some(split_ws_def_id) = tyckres.type_dependent_def_id(expr.hir_id)
             && cx.tcx.is_diagnostic_item(sym::str_split_whitespace, split_ws_def_id)
             && let ExprKind::MethodCall(path, _trim_recv, [], trim_span) = split_recv.kind
-            && let trim_fn_name @ ("trim" | "trim_start" | "trim_end") = path.ident.name.as_str()
+            && let trim_fn_name @ (sym::trim | sym::trim_start | sym::trim_end) = path.ident.name
             && let Some(trim_def_id) = tyckres.type_dependent_def_id(split_recv.hir_id)
             && is_one_of_trim_diagnostic_items(cx, trim_def_id)
         {
diff --git a/src/tools/clippy/clippy_lints/src/swap.rs b/src/tools/clippy/clippy_lints/src/swap.rs
index e3ecd6508bf..5ecbb56925e 100644
--- a/src/tools/clippy/clippy_lints/src/swap.rs
+++ b/src/tools/clippy/clippy_lints/src/swap.rs
@@ -3,7 +3,7 @@ use clippy_utils::source::{snippet_indent, 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, is_in_const_context, std_or_core};
+use clippy_utils::{can_mut_borrow_both, eq_expr_value, is_in_const_context, path_to_local, std_or_core};
 use itertools::Itertools;
 
 use rustc_data_structures::fx::FxIndexSet;
@@ -350,12 +350,21 @@ impl<'tcx> IndexBinding<'_, 'tcx> {
                 format!("{lhs_snippet}{rhs_snippet}")
             },
             ExprKind::Path(QPath::Resolved(_, path)) => {
-                let init = self.cx.expr_or_init(expr);
-
                 let Some(first_segment) = path.segments.first() else {
                     return String::new();
                 };
-                if !self.suggest_span.contains(init.span) || !self.is_used_other_than_swapping(first_segment.ident) {
+
+                let init = self.cx.expr_or_init(expr);
+
+                // We skip suggesting a variable binding in any of these cases:
+                // - Variable initialization is outside the suggestion span
+                // - Variable declaration is outside the suggestion span
+                // - Variable is not used as an index or elsewhere later
+                if !self.suggest_span.contains(init.span)
+                    || path_to_local(expr)
+                        .is_some_and(|hir_id| !self.suggest_span.contains(self.cx.tcx.hir_span(hir_id)))
+                    || !self.is_used_other_than_swapping(first_segment.ident)
+                {
                     return String::new();
                 }
 
diff --git a/src/tools/clippy/clippy_lints/src/transmute/missing_transmute_annotations.rs b/src/tools/clippy/clippy_lints/src/transmute/missing_transmute_annotations.rs
index 96286fcf73d..08f36a2ed5d 100644
--- a/src/tools/clippy/clippy_lints/src/transmute/missing_transmute_annotations.rs
+++ b/src/tools/clippy/clippy_lints/src/transmute/missing_transmute_annotations.rs
@@ -74,7 +74,7 @@ pub(super) fn check<'tcx>(
         last.ident.span.with_hi(path.span.hi()),
         "transmute used without annotations",
         "consider adding missing annotations",
-        format!("{}::<{from_ty}, {to_ty}>", last.ident.as_str()),
+        format!("{}::<{from_ty}, {to_ty}>", last.ident),
         Applicability::MaybeIncorrect,
     );
     true
diff --git a/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs b/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs
index 004ad03e708..4b23367645e 100644
--- a/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs
+++ b/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs
@@ -33,7 +33,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, lt: &Lifetime, m
                 let ltopt = if lt.is_anonymous() {
                     String::new()
                 } else {
-                    format!("{} ", lt.ident.as_str())
+                    format!("{} ", lt.ident)
                 };
 
                 if mut_ty.mutbl == Mutability::Mut {
diff --git a/src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs b/src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs
index 019ae16ca85..ae6d8a1c1aa 100644
--- a/src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs
+++ b/src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs
@@ -1,9 +1,14 @@
+use std::iter;
+
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::is_from_proc_macro;
-use clippy_utils::source::{SourceText, SpanRangeExt, indent_of, reindent_multiline};
+use clippy_utils::source::{SpanRangeExt, indent_of, reindent_multiline};
+use clippy_utils::sugg::Sugg;
+use clippy_utils::ty::expr_type_is_certain;
+use clippy_utils::{is_expr_default, is_from_proc_macro};
 use rustc_errors::Applicability;
 use rustc_hir::{Block, Expr, ExprKind, MatchSource, Node, StmtKind};
 use rustc_lint::LateContext;
+use rustc_span::SyntaxContext;
 
 use super::{UNIT_ARG, utils};
 
@@ -59,7 +64,7 @@ fn is_questionmark_desugar_marked_call(expr: &Expr<'_>) -> bool {
     }
 }
 
-fn lint_unit_args(cx: &LateContext<'_>, expr: &Expr<'_>, args_to_recover: &[&Expr<'_>]) {
+fn lint_unit_args<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, args_to_recover: &[&'tcx Expr<'tcx>]) {
     let mut applicability = Applicability::MachineApplicable;
     let (singular, plural) = if args_to_recover.len() > 1 {
         ("", "s")
@@ -100,34 +105,41 @@ fn lint_unit_args(cx: &LateContext<'_>, expr: &Expr<'_>, args_to_recover: &[&Exp
 
             let arg_snippets: Vec<_> = args_to_recover
                 .iter()
-                .filter_map(|arg| arg.span.get_source_text(cx))
+                // If the argument is from an expansion and is a `Default::default()`, we skip it
+                .filter(|arg| !arg.span.from_expansion() || !is_expr_default_nested(cx, arg))
+                .filter_map(|arg| get_expr_snippet(cx, arg))
                 .collect();
-            let arg_snippets_without_empty_blocks: Vec<_> = args_to_recover
+
+            // If the argument is an empty block or `Default::default()`, we can replace it with `()`.
+            let arg_snippets_without_redundant_exprs: Vec<_> = args_to_recover
                 .iter()
-                .filter(|arg| !is_empty_block(arg))
-                .filter_map(|arg| arg.span.get_source_text(cx))
+                .filter(|arg| !is_expr_default_nested(cx, arg) && (arg.span.from_expansion() || !is_empty_block(arg)))
+                .filter_map(|arg| get_expr_snippet_with_type_certainty(cx, arg))
                 .collect();
 
             if let Some(call_snippet) = expr.span.get_source_text(cx) {
-                let sugg = fmt_stmts_and_call(
-                    cx,
-                    expr,
-                    &call_snippet,
-                    &arg_snippets,
-                    &arg_snippets_without_empty_blocks,
-                );
-
-                if arg_snippets_without_empty_blocks.is_empty() {
+                if arg_snippets_without_redundant_exprs.is_empty()
+                    && let suggestions = args_to_recover
+                        .iter()
+                        .filter(|arg| !arg.span.from_expansion() || !is_expr_default_nested(cx, arg))
+                        .map(|arg| (arg.span.parent_callsite().unwrap_or(arg.span), "()".to_string()))
+                        .collect::<Vec<_>>()
+                    && !suggestions.is_empty()
+                {
                     db.multipart_suggestion(
                         format!("use {singular}unit literal{plural} instead"),
-                        args_to_recover
-                            .iter()
-                            .map(|arg| (arg.span, "()".to_string()))
-                            .collect::<Vec<_>>(),
+                        suggestions,
                         applicability,
                     );
                 } else {
-                    let plural = arg_snippets_without_empty_blocks.len() > 1;
+                    let plural = arg_snippets_without_redundant_exprs.len() > 1;
+                    let sugg = fmt_stmts_and_call(
+                        cx,
+                        expr,
+                        &call_snippet,
+                        arg_snippets,
+                        arg_snippets_without_redundant_exprs,
+                    );
                     let empty_or_s = if plural { "s" } else { "" };
                     let it_or_them = if plural { "them" } else { "it" };
                     db.span_suggestion(
@@ -144,6 +156,55 @@ fn lint_unit_args(cx: &LateContext<'_>, expr: &Expr<'_>, args_to_recover: &[&Exp
     );
 }
 
+fn is_expr_default_nested<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
+    is_expr_default(cx, expr)
+        || matches!(expr.kind, ExprKind::Block(block, _)
+        if block.expr.is_some() && is_expr_default_nested(cx, block.expr.unwrap()))
+}
+
+enum MaybeTypeUncertain<'tcx> {
+    Certain(Sugg<'tcx>),
+    Uncertain(Sugg<'tcx>),
+}
+
+impl From<MaybeTypeUncertain<'_>> for String {
+    fn from(value: MaybeTypeUncertain<'_>) -> Self {
+        match value {
+            MaybeTypeUncertain::Certain(sugg) => sugg.to_string(),
+            MaybeTypeUncertain::Uncertain(sugg) => format!("let _: () = {sugg}"),
+        }
+    }
+}
+
+fn get_expr_snippet<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<Sugg<'tcx>> {
+    let mut app = Applicability::MachineApplicable;
+    let snip = Sugg::hir_with_context(cx, expr, SyntaxContext::root(), "..", &mut app);
+    if app != Applicability::MachineApplicable {
+        return None;
+    }
+
+    Some(snip)
+}
+
+fn get_expr_snippet_with_type_certainty<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx Expr<'tcx>,
+) -> Option<MaybeTypeUncertain<'tcx>> {
+    get_expr_snippet(cx, expr).map(|snip| {
+        // If the type of the expression is certain, we can use it directly.
+        // Otherwise, we wrap it in a `let _: () = ...` to ensure the type is correct.
+        if !expr_type_is_certain(cx, expr) && !is_block_with_no_expr(expr) {
+            MaybeTypeUncertain::Uncertain(snip)
+        } else {
+            MaybeTypeUncertain::Certain(snip)
+        }
+    })
+}
+
+fn is_block_with_no_expr(expr: &Expr<'_>) -> bool {
+    matches!(expr.kind, ExprKind::Block(Block { expr: None, .. }, _))
+}
+
 fn is_empty_block(expr: &Expr<'_>) -> bool {
     matches!(
         expr.kind,
@@ -162,23 +223,20 @@ fn fmt_stmts_and_call(
     cx: &LateContext<'_>,
     call_expr: &Expr<'_>,
     call_snippet: &str,
-    args_snippets: &[SourceText],
-    non_empty_block_args_snippets: &[SourceText],
+    args_snippets: Vec<Sugg<'_>>,
+    non_empty_block_args_snippets: Vec<MaybeTypeUncertain<'_>>,
 ) -> String {
     let call_expr_indent = indent_of(cx, call_expr.span).unwrap_or(0);
-    let call_snippet_with_replacements = args_snippets
-        .iter()
-        .fold(call_snippet.to_owned(), |acc, arg| acc.replacen(arg.as_ref(), "()", 1));
+    let call_snippet_with_replacements = args_snippets.into_iter().fold(call_snippet.to_owned(), |acc, arg| {
+        acc.replacen(&arg.to_string(), "()", 1)
+    });
 
-    let mut stmts_and_call = non_empty_block_args_snippets
-        .iter()
-        .map(|it| it.as_ref().to_owned())
-        .collect::<Vec<_>>();
-    stmts_and_call.push(call_snippet_with_replacements);
-    stmts_and_call = stmts_and_call
+    let stmts_and_call = non_empty_block_args_snippets
         .into_iter()
+        .map(Into::into)
+        .chain(iter::once(call_snippet_with_replacements))
         .map(|v| reindent_multiline(&v, true, Some(call_expr_indent)))
-        .collect();
+        .collect::<Vec<_>>();
 
     let mut stmts_and_call_snippet = stmts_and_call.join(&format!("{}{}", ";\n", " ".repeat(call_expr_indent)));
     // expr is not in a block statement or result expression position, wrap in a block
diff --git a/src/tools/clippy/clippy_lints/src/unused_io_amount.rs b/src/tools/clippy/clippy_lints/src/unused_io_amount.rs
index 5e1cb9e54f5..12cc1093899 100644
--- a/src/tools/clippy/clippy_lints/src/unused_io_amount.rs
+++ b/src/tools/clippy/clippy_lints/src/unused_io_amount.rs
@@ -1,11 +1,11 @@
 use clippy_utils::diagnostics::span_lint_hir_and_then;
 use clippy_utils::macros::{is_panic, root_macro_call_first_node};
-use clippy_utils::{is_res_lang_ctor, paths, peel_blocks};
+use clippy_utils::{is_res_lang_ctor, paths, peel_blocks, sym};
 use hir::{ExprKind, HirId, PatKind};
 use rustc_hir as hir;
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
-use rustc_span::{Span, sym};
+use rustc_span::Span;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -232,8 +232,16 @@ fn is_unreachable_or_panic(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
 fn unpack_call_chain<'a>(mut expr: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> {
     while let ExprKind::MethodCall(path, receiver, ..) = expr.kind {
         if matches!(
-            path.ident.as_str(),
-            "unwrap" | "expect" | "unwrap_or" | "unwrap_or_else" | "ok" | "is_ok" | "is_err" | "or_else" | "or"
+            path.ident.name,
+            sym::unwrap
+                | sym::expect
+                | sym::unwrap_or
+                | sym::unwrap_or_else
+                | sym::ok
+                | sym::is_ok
+                | sym::is_err
+                | sym::or_else
+                | sym::or
         ) {
             expr = receiver;
         } else {
diff --git a/src/tools/clippy/clippy_lints/src/unused_result_ok.rs b/src/tools/clippy/clippy_lints/src/unused_result_ok.rs
index 958f19d1833..f5ed10fb760 100644
--- a/src/tools/clippy/clippy_lints/src/unused_result_ok.rs
+++ b/src/tools/clippy/clippy_lints/src/unused_result_ok.rs
@@ -1,11 +1,11 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet_with_context;
+use clippy_utils::sym;
 use clippy_utils::ty::is_type_diagnostic_item;
 use rustc_errors::Applicability;
 use rustc_hir::{ExprKind, Stmt, StmtKind};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_session::declare_lint_pass;
-use rustc_span::sym;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -36,7 +36,7 @@ impl LateLintPass<'_> for UnusedResultOk {
     fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &Stmt<'_>) {
         if let StmtKind::Semi(expr) = stmt.kind
             && let ExprKind::MethodCall(ok_path, recv, [], ..) = expr.kind //check is expr.ok() has type Result<T,E>.ok(, _)
-            && ok_path.ident.as_str() == "ok"
+            && ok_path.ident.name == sym::ok
             && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result)
             && !stmt.span.in_external_macro(cx.sess().source_map())
         {
diff --git a/src/tools/clippy/clippy_lints/src/unused_unit.rs b/src/tools/clippy/clippy_lints/src/unused_unit.rs
index 9859ddfdf7b..3811f0fe6b5 100644
--- a/src/tools/clippy/clippy_lints/src/unused_unit.rs
+++ b/src/tools/clippy/clippy_lints/src/unused_unit.rs
@@ -100,7 +100,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedUnit {
         let segments = &poly.trait_ref.path.segments;
 
         if segments.len() == 1
-            && ["Fn", "FnMut", "FnOnce"].contains(&segments[0].ident.name.as_str())
+            && matches!(segments[0].ident.name, sym::Fn | sym::FnMut | sym::FnOnce)
             && let Some(args) = segments[0].args
             && args.parenthesized == GenericArgsParentheses::ParenSugar
             && let constraints = &args.constraints
@@ -109,6 +109,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedUnit {
             && let AssocItemConstraintKind::Equality { term: Term::Ty(hir_ty) } = constraints[0].kind
             && args.span_ext.hi() != poly.span.hi()
             && !hir_ty.span.from_expansion()
+            && args.span_ext.hi() != hir_ty.span.hi()
             && is_unit_ty(hir_ty)
         {
             lint_unneeded_unit_return(cx, hir_ty.span, poly.span);
diff --git a/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs b/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs
index 3c23662e9d1..8d873536295 100644
--- a/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs
+++ b/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs
@@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::higher::{VecInitKind, get_vec_init_kind};
 use clippy_utils::source::snippet;
 use clippy_utils::visitors::for_each_local_use_after_expr;
-use clippy_utils::{get_parent_expr, path_to_local_id};
+use clippy_utils::{get_parent_expr, path_to_local_id, sym};
 use core::ops::ControlFlow;
 use rustc_errors::Applicability;
 use rustc_hir::def::Res;
@@ -202,7 +202,7 @@ impl<'tcx> LateLintPass<'tcx> for VecInitThenPush {
             if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = stmt.kind
                 && let ExprKind::MethodCall(name, self_arg, [_], _) = expr.kind
                 && path_to_local_id(self_arg, searcher.local_id)
-                && name.ident.as_str() == "push"
+                && name.ident.name == sym::push
             {
                 self.searcher = Some(VecPushSearcher {
                     err_span: searcher.err_span.to(stmt.span),
diff --git a/src/tools/clippy/clippy_lints/src/wildcard_imports.rs b/src/tools/clippy/clippy_lints/src/wildcard_imports.rs
index 467811c586b..d9dda6eadb2 100644
--- a/src/tools/clippy/clippy_lints/src/wildcard_imports.rs
+++ b/src/tools/clippy/clippy_lints/src/wildcard_imports.rs
@@ -9,8 +9,8 @@ use rustc_hir::{Item, ItemKind, PathSegment, UseKind};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::ty;
 use rustc_session::impl_lint_pass;
+use rustc_span::BytePos;
 use rustc_span::symbol::kw;
-use rustc_span::{BytePos, sym};
 
 declare_clippy_lint! {
     /// ### What it does
@@ -193,9 +193,7 @@ impl WildcardImports {
 // Allow "...prelude::..::*" imports.
 // Many crates have a prelude, and it is imported as a glob by design.
 fn is_prelude_import(segments: &[PathSegment<'_>]) -> bool {
-    segments
-        .iter()
-        .any(|ps| ps.ident.as_str().contains(sym::prelude.as_str()))
+    segments.iter().any(|ps| ps.ident.as_str().contains("prelude"))
 }
 
 // Allow "super::*" imports in tests.
diff --git a/src/tools/clippy/clippy_lints/src/write.rs b/src/tools/clippy/clippy_lints/src/write.rs
index a8758b65c91..d9a007635ca 100644
--- a/src/tools/clippy/clippy_lints/src/write.rs
+++ b/src/tools/clippy/clippy_lints/src/write.rs
@@ -5,8 +5,8 @@ use clippy_utils::source::{SpanRangeExt, expand_past_previous_comma};
 use clippy_utils::{is_in_test, sym};
 use rustc_ast::token::LitKind;
 use rustc_ast::{
-    FormatArgPosition, FormatArgPositionKind, FormatArgs, FormatArgsPiece, FormatOptions, FormatPlaceholder,
-    FormatTrait,
+    FormatArgPosition, FormatArgPositionKind, FormatArgs, FormatArgsPiece, FormatCount, FormatOptions,
+    FormatPlaceholder, FormatTrait,
 };
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, Impl, Item, ItemKind};
@@ -556,12 +556,7 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) {
     // Decrement the index of the remaining by the number of replaced positional arguments
     if !suggestion.is_empty() {
         for piece in &format_args.template {
-            if let Some((span, index)) = positional_arg_piece_span(piece)
-                && suggestion.iter().all(|(s, _)| *s != span)
-            {
-                let decrement = replaced_position.iter().filter(|i| **i < index).count();
-                suggestion.push((span, format!("{{{}}}", index.saturating_sub(decrement))));
-            }
+            relocalize_format_args_indexes(piece, &mut suggestion, &replaced_position);
         }
     }
 
@@ -574,7 +569,7 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) {
     }
 }
 
-/// Extract Span and its index from the given `piece`, iff it's positional argument.
+/// Extract Span and its index from the given `piece`, if it's positional argument.
 fn positional_arg_piece_span(piece: &FormatArgsPiece) -> Option<(Span, usize)> {
     match piece {
         FormatArgsPiece::Placeholder(FormatPlaceholder {
@@ -591,6 +586,57 @@ fn positional_arg_piece_span(piece: &FormatArgsPiece) -> Option<(Span, usize)> {
     }
 }
 
+/// Relocalizes the indexes of positional arguments in the format string
+fn relocalize_format_args_indexes(
+    piece: &FormatArgsPiece,
+    suggestion: &mut Vec<(Span, String)>,
+    replaced_position: &[usize],
+) {
+    if let FormatArgsPiece::Placeholder(FormatPlaceholder {
+        argument:
+            FormatArgPosition {
+                index: Ok(index),
+                // Only consider positional arguments
+                kind: FormatArgPositionKind::Number,
+                span: Some(span),
+            },
+        format_options,
+        ..
+    }) = piece
+    {
+        if suggestion.iter().any(|(s, _)| s.overlaps(*span)) {
+            // If the span is already in the suggestion, we don't need to process it again
+            return;
+        }
+
+        // lambda to get the decremented index based on the replaced positions
+        let decremented_index = |index: usize| -> usize {
+            let decrement = replaced_position.iter().filter(|&&i| i < index).count();
+            index - decrement
+        };
+
+        suggestion.push((*span, decremented_index(*index).to_string()));
+
+        // If there are format options, we need to handle them as well
+        if *format_options != FormatOptions::default() {
+            // lambda to process width and precision format counts and add them to the suggestion
+            let mut process_format_count = |count: &Option<FormatCount>, formatter: &dyn Fn(usize) -> String| {
+                if let Some(FormatCount::Argument(FormatArgPosition {
+                    index: Ok(format_arg_index),
+                    kind: FormatArgPositionKind::Number,
+                    span: Some(format_arg_span),
+                })) = count
+                {
+                    suggestion.push((*format_arg_span, formatter(decremented_index(*format_arg_index))));
+                }
+            };
+
+            process_format_count(&format_options.width, &|index: usize| format!("{index}$"));
+            process_format_count(&format_options.precision, &|index: usize| format!(".{index}$"));
+        }
+    }
+}
+
 /// Removes the raw marker, `#`s and quotes from a str, and returns if the literal is raw
 ///
 /// `r#"a"#` -> (`a`, true)
diff --git a/src/tools/clippy/clippy_lints/src/zombie_processes.rs b/src/tools/clippy/clippy_lints/src/zombie_processes.rs
index 09f1084fe70..6ab94a52210 100644
--- a/src/tools/clippy/clippy_lints/src/zombie_processes.rs
+++ b/src/tools/clippy/clippy_lints/src/zombie_processes.rs
@@ -5,7 +5,7 @@ use rustc_ast::Mutability;
 use rustc_ast::visit::visit_opt;
 use rustc_errors::Applicability;
 use rustc_hir::def_id::LocalDefId;
-use rustc_hir::intravisit::{Visitor, walk_block, walk_expr, walk_local};
+use rustc_hir::intravisit::{Visitor, walk_block, walk_expr};
 use rustc_hir::{Expr, ExprKind, HirId, LetStmt, Node, PatKind, Stmt, StmtKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::hir::nested_filter;
@@ -69,8 +69,9 @@ impl<'tcx> LateLintPass<'tcx> for ZombieProcesses {
                     let mut vis = WaitFinder {
                         cx,
                         local_id,
+                        create_id: expr.hir_id,
                         body_id: cx.tcx.hir_enclosing_body_owner(expr.hir_id),
-                        state: VisitorState::WalkUpToLocal,
+                        state: VisitorState::WalkUpToCreate,
                         early_return: None,
                         missing_wait_branch: None,
                     };
@@ -131,6 +132,7 @@ struct MaybeWait(Span);
 struct WaitFinder<'a, 'tcx> {
     cx: &'a LateContext<'tcx>,
     local_id: HirId,
+    create_id: HirId,
     body_id: LocalDefId,
     state: VisitorState,
     early_return: Option<Span>,
@@ -141,8 +143,8 @@ struct WaitFinder<'a, 'tcx> {
 
 #[derive(PartialEq)]
 enum VisitorState {
-    WalkUpToLocal,
-    LocalFound,
+    WalkUpToCreate,
+    CreateFound,
 }
 
 #[derive(Copy, Clone)]
@@ -155,19 +157,13 @@ impl<'tcx> Visitor<'tcx> for WaitFinder<'_, 'tcx> {
     type NestedFilter = nested_filter::OnlyBodies;
     type Result = ControlFlow<MaybeWait>;
 
-    fn visit_local(&mut self, l: &'tcx LetStmt<'tcx>) -> Self::Result {
-        if self.state == VisitorState::WalkUpToLocal
-            && let PatKind::Binding(_, pat_id, ..) = l.pat.kind
-            && self.local_id == pat_id
-        {
-            self.state = VisitorState::LocalFound;
+    fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) -> Self::Result {
+        if ex.hir_id == self.create_id {
+            self.state = VisitorState::CreateFound;
+            return Continue(());
         }
 
-        walk_local(self, l)
-    }
-
-    fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) -> Self::Result {
-        if self.state != VisitorState::LocalFound {
+        if self.state != VisitorState::CreateFound {
             return walk_expr(self, ex);
         }
 
diff --git a/src/tools/clippy/clippy_lints_internal/src/almost_standard_lint_formulation.rs b/src/tools/clippy/clippy_lints_internal/src/almost_standard_lint_formulation.rs
index 311f76fee6b..7eeec84720f 100644
--- a/src/tools/clippy/clippy_lints_internal/src/almost_standard_lint_formulation.rs
+++ b/src/tools/clippy/clippy_lints_internal/src/almost_standard_lint_formulation.rs
@@ -45,7 +45,7 @@ impl AlmostStandardFormulation {
 impl<'tcx> LateLintPass<'tcx> for AlmostStandardFormulation {
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
         let mut check_next = false;
-        if let ItemKind::Static(_, ty, Mutability::Not, _) = item.kind {
+        if let ItemKind::Static(Mutability::Not, _, ty, _) = item.kind {
             let lines = cx
                 .tcx
                 .hir_attrs(item.hir_id())
diff --git a/src/tools/clippy/clippy_lints_internal/src/lint_without_lint_pass.rs b/src/tools/clippy/clippy_lints_internal/src/lint_without_lint_pass.rs
index 0edeef3ab85..45a866030b2 100644
--- a/src/tools/clippy/clippy_lints_internal/src/lint_without_lint_pass.rs
+++ b/src/tools/clippy/clippy_lints_internal/src/lint_without_lint_pass.rs
@@ -105,7 +105,7 @@ impl_lint_pass!(LintWithoutLintPass => [
 
 impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass {
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
-        if let hir::ItemKind::Static(ident, ty, Mutability::Not, body_id) = item.kind {
+        if let hir::ItemKind::Static(Mutability::Not, ident, ty, body_id) = item.kind {
             if is_lint_ref_type(cx, ty) {
                 check_invalid_clippy_version_attribute(cx, item);
 
diff --git a/src/tools/clippy/clippy_utils/README.md b/src/tools/clippy/clippy_utils/README.md
index efbacbd72db..1aa16e3943c 100644
--- a/src/tools/clippy/clippy_utils/README.md
+++ b/src/tools/clippy/clippy_utils/README.md
@@ -8,7 +8,7 @@ This crate is only guaranteed to build with this `nightly` toolchain:
 
 <!-- begin autogenerated nightly -->
 ```
-nightly-2025-05-31
+nightly-2025-06-12
 ```
 <!-- end autogenerated nightly -->
 
diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs
index f6ef638e618..c7a2375c8df 100644
--- a/src/tools/clippy/clippy_utils/src/lib.rs
+++ b/src/tools/clippy/clippy_utils/src/lib.rs
@@ -1793,10 +1793,9 @@ pub fn in_automatically_derived(tcx: TyCtxt<'_>, id: HirId) -> bool {
 
 /// Checks if the given `DefId` matches the `libc` item.
 pub fn match_libc_symbol(cx: &LateContext<'_>, did: DefId, name: Symbol) -> bool {
-    let path = cx.get_def_path(did);
     // libc is meant to be used as a flat list of names, but they're all actually defined in different
     // modules based on the target platform. Ignore everything but crate name and the item name.
-    path.first().is_some_and(|s| *s == sym::libc) && path.last().copied() == Some(name)
+    cx.tcx.crate_name(did.krate) == sym::libc && cx.tcx.def_path_str(did).ends_with(name.as_str())
 }
 
 /// Returns the list of condition expressions and the list of blocks in a
@@ -3473,3 +3472,15 @@ pub fn desugar_await<'tcx>(expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
         None
     }
 }
+
+/// Checks if the given expression is a call to `Default::default()`.
+pub fn is_expr_default<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
+    if let ExprKind::Call(fn_expr, []) = &expr.kind
+        && let ExprKind::Path(qpath) = &fn_expr.kind
+        && let Res::Def(_, def_id) = cx.qpath_res(qpath, fn_expr.hir_id)
+    {
+        cx.tcx.is_diagnostic_item(sym::default_fn, def_id)
+    } else {
+        false
+    }
+}
diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs
index 8e16f943e9f..e629012b187 100644
--- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs
+++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs
@@ -18,7 +18,7 @@ use rustc_middle::mir::{
 };
 use rustc_middle::traits::{BuiltinImplSource, ImplSource, ObligationCause};
 use rustc_middle::ty::adjustment::PointerCoercion;
-use rustc_middle::ty::{self, GenericArgKind, TraitRef, Ty, TyCtxt};
+use rustc_middle::ty::{self, GenericArgKind, Instance, TraitRef, Ty, TyCtxt};
 use rustc_span::Span;
 use rustc_span::symbol::sym;
 use rustc_trait_selection::traits::{ObligationCtxt, SelectionContext};
@@ -349,7 +349,15 @@ fn check_terminator<'tcx>(
         }
         | TerminatorKind::TailCall { func, args, fn_span: _ } => {
             let fn_ty = func.ty(body, cx.tcx);
-            if let ty::FnDef(fn_def_id, _) = *fn_ty.kind() {
+            if let ty::FnDef(fn_def_id, fn_substs) = fn_ty.kind() {
+                // FIXME: when analyzing a function with generic parameters, we may not have enough information to
+                // resolve to an instance. However, we could check if a host effect predicate can guarantee that
+                // this can be made a `const` call.
+                let fn_def_id = match Instance::try_resolve(cx.tcx, cx.typing_env(), *fn_def_id, fn_substs) {
+                    Ok(Some(fn_inst)) => fn_inst.def_id(),
+                    Ok(None) => return Err((span, format!("cannot resolve instance for {func:?}").into())),
+                    Err(_) => return Err((span, format!("error during instance resolution of {func:?}").into())),
+                };
                 if !is_stable_const_fn(cx, fn_def_id, msrv) {
                     return Err((
                         span,
diff --git a/src/tools/clippy/clippy_utils/src/sym.rs b/src/tools/clippy/clippy_utils/src/sym.rs
index f417530be36..3b58dba5628 100644
--- a/src/tools/clippy/clippy_utils/src/sym.rs
+++ b/src/tools/clippy/clippy_utils/src/sym.rs
@@ -76,7 +76,6 @@ generate! {
     Visitor,
     Weak,
     abs,
-    align_of,
     ambiguous_glob_reexports,
     append,
     arg,
@@ -84,6 +83,7 @@ generate! {
     as_deref,
     as_deref_mut,
     as_mut,
+    as_path,
     assert_failed,
     author,
     borrow,
@@ -121,6 +121,8 @@ generate! {
     copy_to,
     copy_to_nonoverlapping,
     count_ones,
+    create,
+    create_new,
     cycle,
     cyclomatic_complexity,
     de,
@@ -132,7 +134,6 @@ generate! {
     enum_glob_use,
     enumerate,
     err,
-    error,
     exp,
     expect_err,
     expn_data,
@@ -150,8 +151,11 @@ generate! {
     floor_char_boundary,
     fold,
     for_each,
+    from_be_bytes,
     from_bytes_with_nul,
     from_bytes_with_nul_unchecked,
+    from_le_bytes,
+    from_ne_bytes,
     from_ptr,
     from_raw,
     from_ref,
@@ -184,8 +188,10 @@ generate! {
     is_err,
     is_file,
     is_none,
+    is_none_or,
     is_ok,
     is_some,
+    is_some_and,
     isqrt,
     itertools,
     join,
@@ -252,9 +258,12 @@ generate! {
     powi,
     product,
     push,
+    read,
+    read_exact,
     read_line,
     read_to_end,
     read_to_string,
+    read_unaligned,
     redundant_pub_crate,
     regex,
     rem_euclid,
@@ -323,16 +332,22 @@ generate! {
     then_some,
     to_ascii_lowercase,
     to_ascii_uppercase,
+    to_be_bytes,
     to_digit,
+    to_le_bytes,
     to_lowercase,
+    to_ne_bytes,
     to_os_string,
     to_owned,
     to_path_buf,
     to_uppercase,
     tokio,
     trim,
+    trim_end,
     trim_end_matches,
+    trim_start,
     trim_start_matches,
+    truncate,
     unreachable_pub,
     unsafe_removed_from_name,
     unused,
@@ -347,12 +362,15 @@ generate! {
     unwrap_unchecked,
     unzip,
     utils,
+    visit_str,
+    visit_string,
     wake,
     warnings,
     wildcard_imports,
     with_capacity,
     wrapping_offset,
     write,
+    write_unaligned,
     writeln,
     zip,
 }
diff --git a/src/tools/clippy/clippy_utils/src/ty/mod.rs b/src/tools/clippy/clippy_utils/src/ty/mod.rs
index 61e70b3fa0b..1f0a0f2b02a 100644
--- a/src/tools/clippy/clippy_utils/src/ty/mod.rs
+++ b/src/tools/clippy/clippy_utils/src/ty/mod.rs
@@ -1137,21 +1137,38 @@ impl<'tcx> InteriorMut<'tcx> {
 
     /// Check if given type has interior mutability such as [`std::cell::Cell`] or
     /// [`std::cell::RefCell`] etc. and if it does, returns a chain of types that causes
-    /// this type to be interior mutable
+    /// this type to be interior mutable.  False negatives may be expected for infinitely recursive
+    /// types, and `None` will be returned there.
     pub fn interior_mut_ty_chain(&mut self, cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<&'tcx ty::List<Ty<'tcx>>> {
+        self.interior_mut_ty_chain_inner(cx, ty, 0)
+    }
+
+    fn interior_mut_ty_chain_inner(
+        &mut self,
+        cx: &LateContext<'tcx>,
+        ty: Ty<'tcx>,
+        depth: usize,
+    ) -> Option<&'tcx ty::List<Ty<'tcx>>> {
+        if !cx.tcx.recursion_limit().value_within_limit(depth) {
+            return None;
+        }
+
         match self.tys.entry(ty) {
             Entry::Occupied(o) => return *o.get(),
             // Temporarily insert a `None` to break cycles
             Entry::Vacant(v) => v.insert(None),
         };
+        let depth = depth + 1;
 
         let chain = match *ty.kind() {
-            ty::RawPtr(inner_ty, _) if !self.ignore_pointers => self.interior_mut_ty_chain(cx, inner_ty),
-            ty::Ref(_, inner_ty, _) | ty::Slice(inner_ty) => self.interior_mut_ty_chain(cx, inner_ty),
+            ty::RawPtr(inner_ty, _) if !self.ignore_pointers => self.interior_mut_ty_chain_inner(cx, inner_ty, depth),
+            ty::Ref(_, inner_ty, _) | ty::Slice(inner_ty) => self.interior_mut_ty_chain_inner(cx, inner_ty, depth),
             ty::Array(inner_ty, size) if size.try_to_target_usize(cx.tcx) != Some(0) => {
-                self.interior_mut_ty_chain(cx, inner_ty)
+                self.interior_mut_ty_chain_inner(cx, inner_ty, depth)
             },
-            ty::Tuple(fields) => fields.iter().find_map(|ty| self.interior_mut_ty_chain(cx, ty)),
+            ty::Tuple(fields) => fields
+                .iter()
+                .find_map(|ty| self.interior_mut_ty_chain_inner(cx, ty, depth)),
             ty::Adt(def, _) if def.is_unsafe_cell() => Some(ty::List::empty()),
             ty::Adt(def, args) => {
                 let is_std_collection = matches!(
@@ -1171,16 +1188,17 @@ impl<'tcx> InteriorMut<'tcx> {
 
                 if is_std_collection || def.is_box() {
                     // Include the types from std collections that are behind pointers internally
-                    args.types().find_map(|ty| self.interior_mut_ty_chain(cx, ty))
+                    args.types()
+                        .find_map(|ty| self.interior_mut_ty_chain_inner(cx, ty, depth))
                 } else if self.ignored_def_ids.contains(&def.did()) || def.is_phantom_data() {
                     None
                 } else {
                     def.all_fields()
-                        .find_map(|f| self.interior_mut_ty_chain(cx, f.ty(cx.tcx, args)))
+                        .find_map(|f| self.interior_mut_ty_chain_inner(cx, f.ty(cx.tcx, args), depth))
                 }
             },
             ty::Alias(ty::Projection, _) => match cx.tcx.try_normalize_erasing_regions(cx.typing_env(), ty) {
-                Ok(normalized_ty) if ty != normalized_ty => self.interior_mut_ty_chain(cx, normalized_ty),
+                Ok(normalized_ty) if ty != normalized_ty => self.interior_mut_ty_chain_inner(cx, normalized_ty, depth),
                 _ => None,
             },
             _ => None,
@@ -1342,7 +1360,8 @@ pub fn has_non_owning_mutable_access<'tcx>(cx: &LateContext<'tcx>, iter_ty: Ty<'
                 mutability.is_mut() || !pointee_ty.is_freeze(cx.tcx, cx.typing_env())
             },
             ty::Closure(_, closure_args) => {
-                matches!(closure_args.types().next_back(), Some(captures) if has_non_owning_mutable_access_inner(cx, phantoms, captures))
+                matches!(closure_args.types().next_back(),
+                         Some(captures) if has_non_owning_mutable_access_inner(cx, phantoms, captures))
             },
             ty::Tuple(tuple_args) => tuple_args
                 .iter()
diff --git a/src/tools/clippy/rust-toolchain.toml b/src/tools/clippy/rust-toolchain.toml
index b6817d9a146..3fc5a1224a8 100644
--- a/src/tools/clippy/rust-toolchain.toml
+++ b/src/tools/clippy/rust-toolchain.toml
@@ -1,6 +1,6 @@
 [toolchain]
 # begin autogenerated nightly
-channel = "nightly-2025-05-31"
+channel = "nightly-2025-06-12"
 # end autogenerated nightly
 components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
 profile = "minimal"
diff --git a/src/tools/clippy/tests/compile-test.rs b/src/tools/clippy/tests/compile-test.rs
index 78b27e2f613..99a01257a7b 100644
--- a/src/tools/clippy/tests/compile-test.rs
+++ b/src/tools/clippy/tests/compile-test.rs
@@ -273,10 +273,10 @@ fn run_ui_cargo(cx: &TestContext) {
     config.program.input_file_flag = CommandBuilder::cargo().input_file_flag;
     config.program.out_dir_flag = CommandBuilder::cargo().out_dir_flag;
     config.program.args = vec!["clippy".into(), "--color".into(), "never".into(), "--quiet".into()];
-    config
-        .program
-        .envs
-        .push(("RUSTFLAGS".into(), Some("-Dwarnings".into())));
+    config.program.envs.extend([
+        ("RUSTFLAGS".into(), Some("-Dwarnings".into())),
+        ("CARGO_INCREMENTAL".into(), Some("0".into())),
+    ]);
     // We need to do this while we still have a rustc in the `program` field.
     config.fill_host_and_target().unwrap();
     config.program.program.set_file_name(if cfg!(windows) {
diff --git a/src/tools/clippy/tests/symbols-used.rs b/src/tools/clippy/tests/symbols-used.rs
new file mode 100644
index 00000000000..bc0456711fb
--- /dev/null
+++ b/src/tools/clippy/tests/symbols-used.rs
@@ -0,0 +1,81 @@
+// This test checks that all symbols defined in Clippy's `sym.rs` file
+// are used in Clippy. Otherwise, it will fail with a list of symbols
+// which are unused.
+//
+// This test is a no-op if run as part of the compiler test suite
+// and will always succeed.
+
+use std::collections::HashSet;
+use std::ffi::OsStr;
+use std::fs;
+
+use regex::Regex;
+use walkdir::{DirEntry, WalkDir};
+
+const SYM_FILE: &str = "clippy_utils/src/sym.rs";
+
+type Result<T, E = AnyError> = std::result::Result<T, E>;
+type AnyError = Box<dyn std::error::Error>;
+
+#[test]
+#[allow(clippy::case_sensitive_file_extension_comparisons)]
+fn all_symbols_are_used() -> Result<()> {
+    if option_env!("RUSTC_TEST_SUITE").is_some() {
+        return Ok(());
+    }
+
+    // Load all symbols defined in `SYM_FILE`.
+    let content = fs::read_to_string(SYM_FILE)?;
+    let content = content
+        .split_once("generate! {")
+        .ok_or("cannot find symbols start")?
+        .1
+        .split_once("\n}\n")
+        .ok_or("cannot find symbols end")?
+        .0;
+    let mut interned: HashSet<String> = Regex::new(r"(?m)^    (\w+)")
+        .unwrap()
+        .captures_iter(content)
+        .map(|m| m[1].to_owned())
+        .collect();
+
+    // Remove symbols used as `sym::*`.
+    let used_re = Regex::new(r"\bsym::(\w+)\b").unwrap();
+    let rs_ext = OsStr::new("rs");
+    for dir in ["clippy_lints", "clippy_lints_internal", "clippy_utils", "src"] {
+        for file in WalkDir::new(dir)
+            .into_iter()
+            .flatten()
+            .map(DirEntry::into_path)
+            .filter(|p| p.extension() == Some(rs_ext))
+        {
+            for cap in used_re.captures_iter(&fs::read_to_string(file)?) {
+                interned.remove(&cap[1]);
+            }
+        }
+    }
+
+    // Remove symbols used as part of paths.
+    let paths_re = Regex::new(r"path!\(([\w:]+)\)").unwrap();
+    for path in [
+        "clippy_utils/src/paths.rs",
+        "clippy_lints_internal/src/internal_paths.rs",
+    ] {
+        for cap in paths_re.captures_iter(&fs::read_to_string(path)?) {
+            for sym in cap[1].split("::") {
+                interned.remove(sym);
+            }
+        }
+    }
+
+    let mut extra = interned.iter().collect::<Vec<_>>();
+    if !extra.is_empty() {
+        extra.sort_unstable();
+        eprintln!("Unused symbols defined in {SYM_FILE}:");
+        for sym in extra {
+            eprintln!("  - {sym}");
+        }
+        Err(format!("extra symbols found — remove them {SYM_FILE}"))?;
+    }
+    Ok(())
+}
diff --git a/src/tools/clippy/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs b/src/tools/clippy/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs
index 6a9a49324db..4a179cd929f 100644
--- a/src/tools/clippy/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs
+++ b/src/tools/clippy/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs
@@ -1,4 +1,5 @@
 #![warn(clippy::await_holding_invalid_type)]
+#![allow(clippy::ip_constant)]
 use std::net::Ipv4Addr;
 
 async fn bad() -> u32 {
diff --git a/src/tools/clippy/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr b/src/tools/clippy/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr
index deb7f49db9e..c3c88698032 100644
--- a/src/tools/clippy/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr
+++ b/src/tools/clippy/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr
@@ -1,5 +1,5 @@
 error: holding a disallowed type across an await point `std::string::String`
-  --> tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs:5:9
+  --> tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs:6:9
    |
 LL |     let _x = String::from("hello");
    |         ^^
@@ -9,13 +9,13 @@ LL |     let _x = String::from("hello");
    = help: to override `-D warnings` add `#[allow(clippy::await_holding_invalid_type)]`
 
 error: holding a disallowed type across an await point `std::net::Ipv4Addr`
-  --> tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs:11:9
+  --> tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs:12:9
    |
 LL |     let x = Ipv4Addr::new(127, 0, 0, 1);
    |         ^
 
 error: holding a disallowed type across an await point `std::string::String`
-  --> tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs:35:13
+  --> tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs:36:13
    |
 LL |         let _x = String::from("hi!");
    |             ^^
diff --git a/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_bottom.rs b/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_bottom.rs
index 06472a4f5d5..922d30443fc 100644
--- a/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_bottom.rs
+++ b/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_bottom.rs
@@ -239,3 +239,40 @@ fn fp_if_let_issue7054() {
 }
 
 fn main() {}
+
+mod issue14873 {
+    fn foo() -> i32 {
+        todo!()
+    }
+
+    macro_rules! qux {
+        ($a:ident, $b:ident, $condition:expr) => {
+            if $condition {
+                "."
+            } else {
+                ""
+            };
+            $a = foo();
+            $b = foo();
+        };
+    }
+
+    fn share_on_bottom() {
+        let mut a = 0;
+        let mut b = 0;
+        if false {
+            qux!(a, b, a == b);
+        } else {
+            qux!(a, b, a != b);
+        };
+
+        if false {
+            qux!(a, b, a == b);
+            let y = 1;
+        } else {
+            qux!(a, b, a != b);
+            let y = 1;
+            //~^ branches_sharing_code
+        }
+    }
+}
diff --git a/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_bottom.stderr b/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_bottom.stderr
index 648a99c65ed..f437db8b733 100644
--- a/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_bottom.stderr
+++ b/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_bottom.stderr
@@ -157,5 +157,20 @@ LL ~     if x == 17 { b = 1; a = 0x99; } else { }
 LL +     a = 0x99;
    |
 
-error: aborting due to 9 previous errors
+error: all if blocks contain the same code at the end
+  --> tests/ui/branches_sharing_code/shared_at_bottom.rs:274:9
+   |
+LL | /             let y = 1;
+LL | |
+LL | |         }
+   | |_________^
+   |
+   = warning: some moved values might need to be renamed to avoid wrong references
+help: consider moving these statements after the if
+   |
+LL ~         }
+LL +         let y = 1;
+   |
+
+error: aborting due to 10 previous errors
 
diff --git a/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top.rs b/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top.rs
index 694c67d4c85..dcd77679fc6 100644
--- a/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top.rs
+++ b/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top.rs
@@ -124,3 +124,34 @@ fn pf_local_with_inferred_type_issue7053() {
 }
 
 fn main() {}
+
+mod issue14873 {
+    fn foo() -> i32 {
+        todo!()
+    }
+
+    macro_rules! qux {
+        ($a:ident, $b:ident, $condition:expr) => {
+            let $a: i32 = foo();
+            let $b: i32 = foo();
+            if $condition { "." } else { "" }
+        };
+    }
+
+    fn share_on_top() {
+        if false {
+            qux!(a, b, a == b);
+        } else {
+            qux!(a, b, a != b);
+        };
+
+        if false {
+            //~^ branches_sharing_code
+            let x = 1;
+            qux!(a, b, a == b);
+        } else {
+            let x = 1;
+            qux!(a, b, a != b);
+        }
+    }
+}
diff --git a/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top.stderr b/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top.stderr
index d28e9c7af29..30efb98b3f5 100644
--- a/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top.stderr
+++ b/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top.stderr
@@ -125,5 +125,20 @@ note: the lint level is defined here
 LL | #![deny(clippy::branches_sharing_code, clippy::if_same_then_else)]
    |                                        ^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 7 previous errors
+error: all if blocks contain the same code at the start
+  --> tests/ui/branches_sharing_code/shared_at_top.rs:148:9
+   |
+LL | /         if false {
+LL | |
+LL | |             let x = 1;
+   | |______________________^
+   |
+   = warning: some moved values might need to be renamed to avoid wrong references
+help: consider moving these statements before the if
+   |
+LL ~         let x = 1;
+LL +         if false {
+   |
+
+error: aborting due to 8 previous errors
 
diff --git a/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs b/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs
index 75334f70f1f..e848f0601e3 100644
--- a/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs
+++ b/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs
@@ -128,3 +128,42 @@ fn added_note_for_expression_use() -> u32 {
 }
 
 fn main() {}
+
+mod issue14873 {
+    fn foo() -> i32 {
+        todo!()
+    }
+
+    macro_rules! qux {
+        ($a:ident, $b:ident, $condition:expr) => {
+            let mut $a: i32 = foo();
+            let mut $b: i32 = foo();
+            if $condition {
+                "."
+            } else {
+                ""
+            };
+            $a = foo();
+            $b = foo();
+        };
+    }
+
+    fn share_on_top_and_bottom() {
+        if false {
+            qux!(a, b, a == b);
+        } else {
+            qux!(a, b, a != b);
+        };
+
+        if false {
+            //~^ branches_sharing_code
+            let x = 1;
+            qux!(a, b, a == b);
+            let y = 1;
+        } else {
+            let x = 1;
+            qux!(a, b, a != b);
+            let y = 1;
+        }
+    }
+}
diff --git a/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top_and_bottom.stderr b/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top_and_bottom.stderr
index 2200ab45089..40f3453edb9 100644
--- a/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top_and_bottom.stderr
+++ b/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top_and_bottom.stderr
@@ -159,5 +159,31 @@ LL ~     }
 LL +     x * 4
    |
 
-error: aborting due to 5 previous errors
+error: all if blocks contain the same code at both the start and the end
+  --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:158:9
+   |
+LL | /         if false {
+LL | |
+LL | |             let x = 1;
+   | |______________________^
+   |
+note: this code is shared at the end
+  --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:166:9
+   |
+LL | /             let y = 1;
+LL | |         }
+   | |_________^
+   = warning: some moved values might need to be renamed to avoid wrong references
+help: consider moving these statements before the if
+   |
+LL ~         let x = 1;
+LL +         if false {
+   |
+help: consider moving these statements after the if
+   |
+LL ~         }
+LL +         let y = 1;
+   |
+
+error: aborting due to 6 previous errors
 
diff --git a/src/tools/clippy/tests/ui/cmp_null.fixed b/src/tools/clippy/tests/ui/cmp_null.fixed
index 140ddb10aeb..04b8ec50160 100644
--- a/src/tools/clippy/tests/ui/cmp_null.fixed
+++ b/src/tools/clippy/tests/ui/cmp_null.fixed
@@ -33,3 +33,9 @@ fn main() {
     let _ = (x as *const ()).is_null();
     //~^ cmp_null
 }
+
+fn issue15010() {
+    let f: *mut i32 = std::ptr::null_mut();
+    debug_assert!(!f.is_null());
+    //~^ cmp_null
+}
diff --git a/src/tools/clippy/tests/ui/cmp_null.rs b/src/tools/clippy/tests/ui/cmp_null.rs
index 16ed17765da..6f7762e6ae8 100644
--- a/src/tools/clippy/tests/ui/cmp_null.rs
+++ b/src/tools/clippy/tests/ui/cmp_null.rs
@@ -33,3 +33,9 @@ fn main() {
     let _ = x as *const () == ptr::null();
     //~^ cmp_null
 }
+
+fn issue15010() {
+    let f: *mut i32 = std::ptr::null_mut();
+    debug_assert!(f != std::ptr::null_mut());
+    //~^ cmp_null
+}
diff --git a/src/tools/clippy/tests/ui/cmp_null.stderr b/src/tools/clippy/tests/ui/cmp_null.stderr
index 6821846d046..8a75b050111 100644
--- a/src/tools/clippy/tests/ui/cmp_null.stderr
+++ b/src/tools/clippy/tests/ui/cmp_null.stderr
@@ -31,5 +31,11 @@ error: comparing with null is better expressed by the `.is_null()` method
 LL |     let _ = x as *const () == ptr::null();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(x as *const ()).is_null()`
 
-error: aborting due to 5 previous errors
+error: comparing with null is better expressed by the `.is_null()` method
+  --> tests/ui/cmp_null.rs:39:19
+   |
+LL |     debug_assert!(f != std::ptr::null_mut());
+   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `!f.is_null()`
+
+error: aborting due to 6 previous errors
 
diff --git a/src/tools/clippy/tests/ui/coerce_container_to_any.fixed b/src/tools/clippy/tests/ui/coerce_container_to_any.fixed
new file mode 100644
index 00000000000..ae9d3ef9656
--- /dev/null
+++ b/src/tools/clippy/tests/ui/coerce_container_to_any.fixed
@@ -0,0 +1,26 @@
+#![warn(clippy::coerce_container_to_any)]
+
+use std::any::Any;
+
+fn main() {
+    let x: Box<dyn Any> = Box::new(());
+    let ref_x = &x;
+
+    f(&*x);
+    //~^ coerce_container_to_any
+
+    f(&**ref_x);
+    //~^ coerce_container_to_any
+
+    let _: &dyn Any = &*x;
+    //~^ coerce_container_to_any
+
+    f(&42);
+    f(&Box::new(()));
+    f(&Box::new(Box::new(())));
+    f(&**ref_x);
+    f(&*x);
+    let _: &dyn Any = &*x;
+}
+
+fn f(_: &dyn Any) {}
diff --git a/src/tools/clippy/tests/ui/coerce_container_to_any.rs b/src/tools/clippy/tests/ui/coerce_container_to_any.rs
new file mode 100644
index 00000000000..9948bd48e0d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/coerce_container_to_any.rs
@@ -0,0 +1,26 @@
+#![warn(clippy::coerce_container_to_any)]
+
+use std::any::Any;
+
+fn main() {
+    let x: Box<dyn Any> = Box::new(());
+    let ref_x = &x;
+
+    f(&x);
+    //~^ coerce_container_to_any
+
+    f(ref_x);
+    //~^ coerce_container_to_any
+
+    let _: &dyn Any = &x;
+    //~^ coerce_container_to_any
+
+    f(&42);
+    f(&Box::new(()));
+    f(&Box::new(Box::new(())));
+    f(&**ref_x);
+    f(&*x);
+    let _: &dyn Any = &*x;
+}
+
+fn f(_: &dyn Any) {}
diff --git a/src/tools/clippy/tests/ui/coerce_container_to_any.stderr b/src/tools/clippy/tests/ui/coerce_container_to_any.stderr
new file mode 100644
index 00000000000..00ab77e0ce0
--- /dev/null
+++ b/src/tools/clippy/tests/ui/coerce_container_to_any.stderr
@@ -0,0 +1,23 @@
+error: coercing `&std::boxed::Box<dyn std::any::Any>` to `&dyn Any`
+  --> tests/ui/coerce_container_to_any.rs:9:7
+   |
+LL |     f(&x);
+   |       ^^ help: consider dereferencing: `&*x`
+   |
+   = note: `-D clippy::coerce-container-to-any` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::coerce_container_to_any)]`
+
+error: coercing `&std::boxed::Box<dyn std::any::Any>` to `&dyn Any`
+  --> tests/ui/coerce_container_to_any.rs:12:7
+   |
+LL |     f(ref_x);
+   |       ^^^^^ help: consider dereferencing: `&**ref_x`
+
+error: coercing `&std::boxed::Box<dyn std::any::Any>` to `&dyn Any`
+  --> tests/ui/coerce_container_to_any.rs:15:23
+   |
+LL |     let _: &dyn Any = &x;
+   |                       ^^ help: consider dereferencing: `&*x`
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/crashes/ice-14935.rs b/src/tools/clippy/tests/ui/crashes/ice-14935.rs
new file mode 100644
index 00000000000..74cda9aae53
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-14935.rs
@@ -0,0 +1,27 @@
+//@check-pass
+#![warn(clippy::mutable_key_type)]
+
+use std::marker::PhantomData;
+
+trait Group {
+    type ExposantSet: Group;
+}
+
+struct Pow<T: Group> {
+    exposant: Box<Pow<T::ExposantSet>>,
+    _p: PhantomData<T>,
+}
+
+impl<T: Group> Pow<T> {
+    fn is_zero(&self) -> bool {
+        false
+    }
+    fn normalize(&self) {
+        #[expect(clippy::if_same_then_else)]
+        if self.is_zero() {
+        } else if false {
+        }
+    }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-9463.rs b/src/tools/clippy/tests/ui/crashes/ice-9463.rs
index 93808e0f892..cfa6cdac6fa 100644
--- a/src/tools/clippy/tests/ui/crashes/ice-9463.rs
+++ b/src/tools/clippy/tests/ui/crashes/ice-9463.rs
@@ -1,8 +1,7 @@
-#![deny(arithmetic_overflow)]
+//@check-pass
+
 fn main() {
     let _x = -1_i32 >> -1;
-    //~^ ERROR: this arithmetic operation will overflow
+    #[expect(overflowing_literals)]
     let _y = 1u32 >> 10000000000000u32;
-    //~^ ERROR: this arithmetic operation will overflow
-    //~| ERROR: literal out of range
 }
diff --git a/src/tools/clippy/tests/ui/crashes/ice-9463.stderr b/src/tools/clippy/tests/ui/crashes/ice-9463.stderr
deleted file mode 100644
index 9a3a5e444ad..00000000000
--- a/src/tools/clippy/tests/ui/crashes/ice-9463.stderr
+++ /dev/null
@@ -1,29 +0,0 @@
-error: this arithmetic operation will overflow
-  --> tests/ui/crashes/ice-9463.rs:3:14
-   |
-LL |     let _x = -1_i32 >> -1;
-   |              ^^^^^^^^^^^^ attempt to shift right by `-1_i32`, which would overflow
-   |
-note: the lint level is defined here
-  --> tests/ui/crashes/ice-9463.rs:1:9
-   |
-LL | #![deny(arithmetic_overflow)]
-   |         ^^^^^^^^^^^^^^^^^^^
-
-error: this arithmetic operation will overflow
-  --> tests/ui/crashes/ice-9463.rs:5:14
-   |
-LL |     let _y = 1u32 >> 10000000000000u32;
-   |              ^^^^^^^^^^^^^^^^^^^^^^^^^ attempt to shift right by `1316134912_u32`, which would overflow
-
-error: literal out of range for `u32`
-  --> tests/ui/crashes/ice-9463.rs:5:22
-   |
-LL |     let _y = 1u32 >> 10000000000000u32;
-   |                      ^^^^^^^^^^^^^^^^^
-   |
-   = note: the literal `10000000000000u32` does not fit into the type `u32` whose range is `0..=4294967295`
-   = note: `#[deny(overflowing_literals)]` on by default
-
-error: aborting due to 3 previous errors
-
diff --git a/src/tools/clippy/tests/ui/crashes/ice-rust-107877.rs b/src/tools/clippy/tests/ui/crashes/ice-rust-107877.rs
index 55fe418bed1..dccb0cf8ac1 100644
--- a/src/tools/clippy/tests/ui/crashes/ice-rust-107877.rs
+++ b/src/tools/clippy/tests/ui/crashes/ice-rust-107877.rs
@@ -4,6 +4,7 @@
 
 struct Foo;
 
+#[allow(clippy::infallible_try_from)]
 impl<'a> std::convert::TryFrom<&'a String> for Foo {
     type Error = std::convert::Infallible;
 
diff --git a/src/tools/clippy/tests/ui/create_dir.fixed b/src/tools/clippy/tests/ui/create_dir.fixed
index 4a5b1b77be6..d4b8f8b4d07 100644
--- a/src/tools/clippy/tests/ui/create_dir.fixed
+++ b/src/tools/clippy/tests/ui/create_dir.fixed
@@ -7,12 +7,31 @@ fn create_dir() {}
 
 fn main() {
     // Should be warned
-    create_dir_all("foo");
+    std::fs::create_dir_all("foo");
     //~^ create_dir
-    create_dir_all("bar").unwrap();
+    std::fs::create_dir_all("bar").unwrap();
     //~^ create_dir
 
     // Shouldn't be warned
     create_dir();
     std::fs::create_dir_all("foobar");
 }
+
+mod issue14994 {
+    fn with_no_prefix() {
+        use std::fs::create_dir;
+        std::fs::create_dir_all("some/dir").unwrap();
+        //~^ create_dir
+    }
+
+    fn with_fs_prefix() {
+        use std::fs;
+        fs::create_dir_all("/some/dir").unwrap();
+        //~^ create_dir
+    }
+
+    fn with_full_prefix() {
+        std::fs::create_dir_all("/some/dir").unwrap();
+        //~^ create_dir
+    }
+}
diff --git a/src/tools/clippy/tests/ui/create_dir.rs b/src/tools/clippy/tests/ui/create_dir.rs
index bf185ba3a7c..21e0bdba03b 100644
--- a/src/tools/clippy/tests/ui/create_dir.rs
+++ b/src/tools/clippy/tests/ui/create_dir.rs
@@ -16,3 +16,22 @@ fn main() {
     create_dir();
     std::fs::create_dir_all("foobar");
 }
+
+mod issue14994 {
+    fn with_no_prefix() {
+        use std::fs::create_dir;
+        create_dir("some/dir").unwrap();
+        //~^ create_dir
+    }
+
+    fn with_fs_prefix() {
+        use std::fs;
+        fs::create_dir("/some/dir").unwrap();
+        //~^ create_dir
+    }
+
+    fn with_full_prefix() {
+        std::fs::create_dir("/some/dir").unwrap();
+        //~^ create_dir
+    }
+}
diff --git a/src/tools/clippy/tests/ui/create_dir.stderr b/src/tools/clippy/tests/ui/create_dir.stderr
index 51d6ac686e0..e3ca0e7255c 100644
--- a/src/tools/clippy/tests/ui/create_dir.stderr
+++ b/src/tools/clippy/tests/ui/create_dir.stderr
@@ -8,9 +8,8 @@ LL |     std::fs::create_dir("foo");
    = help: to override `-D warnings` add `#[allow(clippy::create_dir)]`
 help: consider calling `std::fs::create_dir_all` instead
    |
-LL -     std::fs::create_dir("foo");
-LL +     create_dir_all("foo");
-   |
+LL |     std::fs::create_dir_all("foo");
+   |                        ++++
 
 error: calling `std::fs::create_dir` where there may be a better way
   --> tests/ui/create_dir.rs:12:5
@@ -20,9 +19,41 @@ LL |     std::fs::create_dir("bar").unwrap();
    |
 help: consider calling `std::fs::create_dir_all` instead
    |
-LL -     std::fs::create_dir("bar").unwrap();
-LL +     create_dir_all("bar").unwrap();
+LL |     std::fs::create_dir_all("bar").unwrap();
+   |                        ++++
+
+error: calling `std::fs::create_dir` where there may be a better way
+  --> tests/ui/create_dir.rs:23:9
+   |
+LL |         create_dir("some/dir").unwrap();
+   |         ^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider calling `std::fs::create_dir_all` instead
+   |
+LL |         std::fs::create_dir_all("some/dir").unwrap();
+   |         +++++++++          ++++
+
+error: calling `std::fs::create_dir` where there may be a better way
+  --> tests/ui/create_dir.rs:29:9
+   |
+LL |         fs::create_dir("/some/dir").unwrap();
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider calling `std::fs::create_dir_all` instead
+   |
+LL |         fs::create_dir_all("/some/dir").unwrap();
+   |                       ++++
+
+error: calling `std::fs::create_dir` where there may be a better way
+  --> tests/ui/create_dir.rs:34:9
+   |
+LL |         std::fs::create_dir("/some/dir").unwrap();
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider calling `std::fs::create_dir_all` instead
    |
+LL |         std::fs::create_dir_all("/some/dir").unwrap();
+   |                            ++++
 
-error: aborting due to 2 previous errors
+error: aborting due to 5 previous errors
 
diff --git a/src/tools/clippy/tests/ui/disallowed_names.rs b/src/tools/clippy/tests/ui/disallowed_names.rs
index 30fbdbc1fdc..15bb6734997 100644
--- a/src/tools/clippy/tests/ui/disallowed_names.rs
+++ b/src/tools/clippy/tests/ui/disallowed_names.rs
@@ -1,3 +1,4 @@
+//@aux-build:proc_macros.rs
 #![allow(
     dead_code,
     clippy::needless_if,
@@ -9,6 +10,9 @@
 )]
 #![warn(clippy::disallowed_names)]
 
+extern crate proc_macros;
+use proc_macros::{external, with_span};
+
 fn test(foo: ()) {}
 //~^ disallowed_names
 
@@ -66,6 +70,17 @@ fn issue_1647_ref_mut() {
     //~^ disallowed_names
 }
 
+pub fn issue_14958_proc_macro() {
+    // does not lint macro-generated code
+    external! {
+        let foo = 0;
+    }
+    with_span! {
+        span
+        let foo = 0;
+    }
+}
+
 #[cfg(test)]
 mod tests {
     fn issue_7305() {
diff --git a/src/tools/clippy/tests/ui/disallowed_names.stderr b/src/tools/clippy/tests/ui/disallowed_names.stderr
index 09398ebbab7..b43d1b3ebfa 100644
--- a/src/tools/clippy/tests/ui/disallowed_names.stderr
+++ b/src/tools/clippy/tests/ui/disallowed_names.stderr
@@ -1,5 +1,5 @@
 error: use of a disallowed/placeholder name `foo`
-  --> tests/ui/disallowed_names.rs:12:9
+  --> tests/ui/disallowed_names.rs:16:9
    |
 LL | fn test(foo: ()) {}
    |         ^^^
@@ -8,79 +8,79 @@ LL | fn test(foo: ()) {}
    = help: to override `-D warnings` add `#[allow(clippy::disallowed_names)]`
 
 error: use of a disallowed/placeholder name `foo`
-  --> tests/ui/disallowed_names.rs:16:9
+  --> tests/ui/disallowed_names.rs:20:9
    |
 LL |     let foo = 42;
    |         ^^^
 
 error: use of a disallowed/placeholder name `baz`
-  --> tests/ui/disallowed_names.rs:19:9
+  --> tests/ui/disallowed_names.rs:23:9
    |
 LL |     let baz = 42;
    |         ^^^
 
 error: use of a disallowed/placeholder name `quux`
-  --> tests/ui/disallowed_names.rs:22:9
+  --> tests/ui/disallowed_names.rs:26:9
    |
 LL |     let quux = 42;
    |         ^^^^
 
 error: use of a disallowed/placeholder name `foo`
-  --> tests/ui/disallowed_names.rs:35:10
+  --> tests/ui/disallowed_names.rs:39:10
    |
 LL |         (foo, Some(baz), quux @ Some(_)) => (),
    |          ^^^
 
 error: use of a disallowed/placeholder name `baz`
-  --> tests/ui/disallowed_names.rs:35:20
+  --> tests/ui/disallowed_names.rs:39:20
    |
 LL |         (foo, Some(baz), quux @ Some(_)) => (),
    |                    ^^^
 
 error: use of a disallowed/placeholder name `quux`
-  --> tests/ui/disallowed_names.rs:35:26
+  --> tests/ui/disallowed_names.rs:39:26
    |
 LL |         (foo, Some(baz), quux @ Some(_)) => (),
    |                          ^^^^
 
 error: use of a disallowed/placeholder name `foo`
-  --> tests/ui/disallowed_names.rs:43:19
+  --> tests/ui/disallowed_names.rs:47:19
    |
 LL | fn issue_1647(mut foo: u8) {
    |                   ^^^
 
 error: use of a disallowed/placeholder name `baz`
-  --> tests/ui/disallowed_names.rs:46:13
+  --> tests/ui/disallowed_names.rs:50:13
    |
 LL |     let mut baz = 0;
    |             ^^^
 
 error: use of a disallowed/placeholder name `quux`
-  --> tests/ui/disallowed_names.rs:49:21
+  --> tests/ui/disallowed_names.rs:53:21
    |
 LL |     if let Some(mut quux) = Some(42) {}
    |                     ^^^^
 
 error: use of a disallowed/placeholder name `baz`
-  --> tests/ui/disallowed_names.rs:54:13
+  --> tests/ui/disallowed_names.rs:58:13
    |
 LL |     let ref baz = 0;
    |             ^^^
 
 error: use of a disallowed/placeholder name `quux`
-  --> tests/ui/disallowed_names.rs:57:21
+  --> tests/ui/disallowed_names.rs:61:21
    |
 LL |     if let Some(ref quux) = Some(42) {}
    |                     ^^^^
 
 error: use of a disallowed/placeholder name `baz`
-  --> tests/ui/disallowed_names.rs:62:17
+  --> tests/ui/disallowed_names.rs:66:17
    |
 LL |     let ref mut baz = 0;
    |                 ^^^
 
 error: use of a disallowed/placeholder name `quux`
-  --> tests/ui/disallowed_names.rs:65:25
+  --> tests/ui/disallowed_names.rs:69:25
    |
 LL |     if let Some(ref mut quux) = Some(42) {}
    |                         ^^^^
diff --git a/src/tools/clippy/tests/ui/doc_suspicious_footnotes.fixed b/src/tools/clippy/tests/ui/doc_suspicious_footnotes.fixed
new file mode 100644
index 00000000000..9ed3fd4ef31
--- /dev/null
+++ b/src/tools/clippy/tests/ui/doc_suspicious_footnotes.fixed
@@ -0,0 +1,186 @@
+#![warn(clippy::doc_suspicious_footnotes)]
+#![allow(clippy::needless_raw_string_hashes)]
+//! This is not a footnote[^1].
+//!
+//! [^1]: <!-- description -->
+//~^ doc_suspicious_footnotes
+//!
+//! This is not a footnote[^either], but it doesn't warn.
+//!
+//! This is not a footnote\[^1], but it also doesn't warn.
+//!
+//! This is not a footnote[^1\], but it also doesn't warn.
+//!
+//! This is not a `footnote[^1]`, but it also doesn't warn.
+//!
+//! This is a footnote[^2].
+//!
+//! [^2]: hello world
+
+/// This is not a footnote[^1].
+///
+/// [^1]: <!-- description -->
+//~^ doc_suspicious_footnotes
+///
+/// This is not a footnote[^either], but it doesn't warn.
+///
+/// This is not a footnote\[^1], but it also doesn't warn.
+///
+/// This is not a footnote[^1\], but it also doesn't warn.
+///
+/// This is not a `footnote[^1]`, but it also doesn't warn.
+///
+/// This is a footnote[^2].
+///
+/// [^2]: hello world
+pub fn footnotes() {
+    // test code goes here
+}
+
+pub struct Foo;
+#[rustfmt::skip]
+impl Foo {
+    #[doc = r#"This is not a footnote[^1].
+
+[^1]: <!-- description -->"#]
+    //~^ doc_suspicious_footnotes
+    #[doc = r#""#]
+    #[doc = r#"This is not a footnote[^either], but it doesn't warn."#]
+    #[doc = r#""#]
+    #[doc = r#"This is not a footnote\[^1], but it also doesn't warn."#]
+    #[doc = r#""#]
+    #[doc = r#"This is not a footnote[^1\], but it also doesn't warn."#]
+    #[doc = r#""#]
+    #[doc = r#"This is not a `footnote[^1]`, but it also doesn't warn."#]
+    #[doc = r#""#]
+    #[doc = r#"This is a footnote[^2]."#]
+    #[doc = r#""#]
+    #[doc = r#"[^2]: hello world"#]
+    pub fn footnotes() {
+        // test code goes here
+    }
+    #[doc = r#"This is not a footnote[^1].
+
+    This is not a footnote[^either], but it doesn't warn.
+
+    This is not a footnote\[^1], but it also doesn't warn.
+
+    This is not a footnote[^1\], but it also doesn't warn.
+
+    This is not a `footnote[^1]`, but it also doesn't warn.
+
+    This is a footnote[^2].
+
+    [^2]: hello world
+    
+
+[^1]: <!-- description -->"#]
+    //~^^^^^^^^^^^^^^ doc_suspicious_footnotes
+    pub fn footnotes2() {
+        // test code goes here
+    }
+    #[cfg_attr(
+        not(FALSE),
+        doc = r#"This is not a footnote[^1].
+
+This is not a footnote[^either], but it doesn't warn.
+
+[^1]: <!-- description -->"#
+    //~^ doc_suspicious_footnotes
+    )]
+    pub fn footnotes3() {
+        // test code goes here
+    }
+    #[doc = "My footnote [^foot\note]"]
+    pub fn footnote4() {
+        // test code goes here
+    }
+    #[doc = "Hihi"]pub fn footnote5() {
+        // test code goes here
+    }
+}
+
+#[doc = r#"This is not a footnote[^1].
+
+[^1]: <!-- description -->"#]
+//~^ doc_suspicious_footnotes
+#[doc = r""]
+#[doc = r"This is not a footnote[^either], but it doesn't warn."]
+#[doc = r""]
+#[doc = r"This is not a footnote\[^1], but it also doesn't warn."]
+#[doc = r""]
+#[doc = r"This is not a footnote[^1\], but it also doesn't warn."]
+#[doc = r""]
+#[doc = r"This is not a `footnote[^1]`, but it also doesn't warn."]
+#[doc = r""]
+#[doc = r"This is a footnote[^2]."]
+#[doc = r""]
+#[doc = r"[^2]: hello world"]
+pub fn footnotes_attrs() {
+    // test code goes here
+}
+
+pub mod multiline {
+    /*!
+     * This is not a footnote[^1]. //~ doc_suspicious_footnotes
+     *
+     * This is not a footnote\[^1], but it doesn't warn.
+     *
+     * This is a footnote[^2].
+     *
+     * These give weird results, but correct ones, so it works.
+     *
+     * [^2]: hello world
+     */
+/*! [^1]: <!-- description --> */
+    /**
+     * This is not a footnote[^1]. //~ doc_suspicious_footnotes
+     *
+     * This is not a footnote\[^1], but it doesn't warn.
+     *
+     * This is a footnote[^2].
+     *
+     * These give weird results, but correct ones, so it works.
+     *
+     * [^2]: hello world
+     */
+/** [^1]: <!-- description --> */
+    pub fn foo() {}
+}
+
+/// This is not a footnote [^1]
+///
+/// [^1]: <!-- description -->
+//~^ doc_suspicious_footnotes
+///
+/// This one is [^2]
+///
+/// [^2]: contents
+#[doc = r#"This is not a footnote [^3]
+
+[^3]: <!-- description -->"#]
+//~^ doc_suspicious_footnotes
+#[doc = ""]
+#[doc = "This one is [^4]"]
+#[doc = ""]
+#[doc = "[^4]: contents"]
+pub struct MultiFragmentFootnote;
+
+#[doc(inline)]
+/// This is not a footnote [^5]
+///
+/// [^5]: <!-- description -->
+//~^ doc_suspicious_footnotes
+///
+/// This one is [^6]
+///
+/// [^6]: contents
+#[doc = r#"This is not a footnote [^7]
+
+[^7]: <!-- description -->"#]
+//~^ doc_suspicious_footnotes
+#[doc = ""]
+#[doc = "This one is [^8]"]
+#[doc = ""]
+#[doc = "[^8]: contents"]
+pub use MultiFragmentFootnote as OtherInlinedFootnote;
diff --git a/src/tools/clippy/tests/ui/doc_suspicious_footnotes.rs b/src/tools/clippy/tests/ui/doc_suspicious_footnotes.rs
new file mode 100644
index 00000000000..9a8d0dcf475
--- /dev/null
+++ b/src/tools/clippy/tests/ui/doc_suspicious_footnotes.rs
@@ -0,0 +1,162 @@
+#![warn(clippy::doc_suspicious_footnotes)]
+#![allow(clippy::needless_raw_string_hashes)]
+//! This is not a footnote[^1].
+//~^ doc_suspicious_footnotes
+//!
+//! This is not a footnote[^either], but it doesn't warn.
+//!
+//! This is not a footnote\[^1], but it also doesn't warn.
+//!
+//! This is not a footnote[^1\], but it also doesn't warn.
+//!
+//! This is not a `footnote[^1]`, but it also doesn't warn.
+//!
+//! This is a footnote[^2].
+//!
+//! [^2]: hello world
+
+/// This is not a footnote[^1].
+//~^ doc_suspicious_footnotes
+///
+/// This is not a footnote[^either], but it doesn't warn.
+///
+/// This is not a footnote\[^1], but it also doesn't warn.
+///
+/// This is not a footnote[^1\], but it also doesn't warn.
+///
+/// This is not a `footnote[^1]`, but it also doesn't warn.
+///
+/// This is a footnote[^2].
+///
+/// [^2]: hello world
+pub fn footnotes() {
+    // test code goes here
+}
+
+pub struct Foo;
+#[rustfmt::skip]
+impl Foo {
+    #[doc = r#"This is not a footnote[^1]."#]
+    //~^ doc_suspicious_footnotes
+    #[doc = r#""#]
+    #[doc = r#"This is not a footnote[^either], but it doesn't warn."#]
+    #[doc = r#""#]
+    #[doc = r#"This is not a footnote\[^1], but it also doesn't warn."#]
+    #[doc = r#""#]
+    #[doc = r#"This is not a footnote[^1\], but it also doesn't warn."#]
+    #[doc = r#""#]
+    #[doc = r#"This is not a `footnote[^1]`, but it also doesn't warn."#]
+    #[doc = r#""#]
+    #[doc = r#"This is a footnote[^2]."#]
+    #[doc = r#""#]
+    #[doc = r#"[^2]: hello world"#]
+    pub fn footnotes() {
+        // test code goes here
+    }
+    #[doc = "This is not a footnote[^1].
+
+    This is not a footnote[^either], but it doesn't warn.
+
+    This is not a footnote\\[^1], but it also doesn't warn.
+
+    This is not a footnote[^1\\], but it also doesn't warn.
+
+    This is not a `footnote[^1]`, but it also doesn't warn.
+
+    This is a footnote[^2].
+
+    [^2]: hello world
+    "]
+    //~^^^^^^^^^^^^^^ doc_suspicious_footnotes
+    pub fn footnotes2() {
+        // test code goes here
+    }
+    #[cfg_attr(
+        not(FALSE),
+        doc = "This is not a footnote[^1].\n\nThis is not a footnote[^either], but it doesn't warn."
+    //~^ doc_suspicious_footnotes
+    )]
+    pub fn footnotes3() {
+        // test code goes here
+    }
+    #[doc = "My footnote [^foot\note]"]
+    pub fn footnote4() {
+        // test code goes here
+    }
+    #[doc = "Hihi"]pub fn footnote5() {
+        // test code goes here
+    }
+}
+
+#[doc = r"This is not a footnote[^1]."]
+//~^ doc_suspicious_footnotes
+#[doc = r""]
+#[doc = r"This is not a footnote[^either], but it doesn't warn."]
+#[doc = r""]
+#[doc = r"This is not a footnote\[^1], but it also doesn't warn."]
+#[doc = r""]
+#[doc = r"This is not a footnote[^1\], but it also doesn't warn."]
+#[doc = r""]
+#[doc = r"This is not a `footnote[^1]`, but it also doesn't warn."]
+#[doc = r""]
+#[doc = r"This is a footnote[^2]."]
+#[doc = r""]
+#[doc = r"[^2]: hello world"]
+pub fn footnotes_attrs() {
+    // test code goes here
+}
+
+pub mod multiline {
+    /*!
+     * This is not a footnote[^1]. //~ doc_suspicious_footnotes
+     *
+     * This is not a footnote\[^1], but it doesn't warn.
+     *
+     * This is a footnote[^2].
+     *
+     * These give weird results, but correct ones, so it works.
+     *
+     * [^2]: hello world
+     */
+    /**
+     * This is not a footnote[^1]. //~ doc_suspicious_footnotes
+     *
+     * This is not a footnote\[^1], but it doesn't warn.
+     *
+     * This is a footnote[^2].
+     *
+     * These give weird results, but correct ones, so it works.
+     *
+     * [^2]: hello world
+     */
+    pub fn foo() {}
+}
+
+/// This is not a footnote [^1]
+//~^ doc_suspicious_footnotes
+///
+/// This one is [^2]
+///
+/// [^2]: contents
+#[doc = "This is not a footnote [^3]"]
+//~^ doc_suspicious_footnotes
+#[doc = ""]
+#[doc = "This one is [^4]"]
+#[doc = ""]
+#[doc = "[^4]: contents"]
+pub struct MultiFragmentFootnote;
+
+#[doc(inline)]
+/// This is not a footnote [^5]
+//~^ doc_suspicious_footnotes
+///
+/// This one is [^6]
+///
+/// [^6]: contents
+#[doc = "This is not a footnote [^7]"]
+//~^ doc_suspicious_footnotes
+#[doc = ""]
+#[doc = "This one is [^8]"]
+#[doc = ""]
+#[doc = "[^8]: contents"]
+pub use MultiFragmentFootnote as OtherInlinedFootnote;
diff --git a/src/tools/clippy/tests/ui/doc_suspicious_footnotes.stderr b/src/tools/clippy/tests/ui/doc_suspicious_footnotes.stderr
new file mode 100644
index 00000000000..4f920f37a62
--- /dev/null
+++ b/src/tools/clippy/tests/ui/doc_suspicious_footnotes.stderr
@@ -0,0 +1,179 @@
+error: looks like a footnote ref, but has no matching footnote
+  --> tests/ui/doc_suspicious_footnotes.rs:3:27
+   |
+LL | //! This is not a footnote[^1].
+   |                           ^^^^
+   |
+   = note: `-D clippy::doc-suspicious-footnotes` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::doc_suspicious_footnotes)]`
+help: add footnote definition
+   |
+LL ~ //! This is not a footnote[^1].
+LL + //!
+LL + //! [^1]: <!-- description -->
+   |
+
+error: looks like a footnote ref, but has no matching footnote
+  --> tests/ui/doc_suspicious_footnotes.rs:18:27
+   |
+LL | /// This is not a footnote[^1].
+   |                           ^^^^
+   |
+help: add footnote definition
+   |
+LL ~ /// This is not a footnote[^1].
+LL + ///
+LL + /// [^1]: <!-- description -->
+   |
+
+error: looks like a footnote ref, but has no matching footnote
+  --> tests/ui/doc_suspicious_footnotes.rs:39:13
+   |
+LL |     #[doc = r#"This is not a footnote[^1]."#]
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: add footnote definition
+   |
+LL ~     #[doc = r#"This is not a footnote[^1].
+LL + 
+LL ~ [^1]: <!-- description -->"#]
+   |
+
+error: looks like a footnote ref, but has no matching footnote
+  --> tests/ui/doc_suspicious_footnotes.rs:56:13
+   |
+LL |       #[doc = "This is not a footnote[^1].
+   |  _____________^
+LL | |
+LL | |     This is not a footnote[^either], but it doesn't warn.
+...  |
+LL | |     [^2]: hello world
+LL | |     "]
+   | |_____^
+   |
+help: add footnote definition
+   |
+LL ~     #[doc = r#"This is not a footnote[^1].
+LL + 
+LL +     This is not a footnote[^either], but it doesn't warn.
+LL + 
+LL +     This is not a footnote\[^1], but it also doesn't warn.
+LL + 
+LL +     This is not a footnote[^1\], but it also doesn't warn.
+LL + 
+LL +     This is not a `footnote[^1]`, but it also doesn't warn.
+LL + 
+LL +     This is a footnote[^2].
+LL + 
+LL +     [^2]: hello world
+LL +     
+LL + 
+LL ~ [^1]: <!-- description -->"#]
+   |
+
+error: looks like a footnote ref, but has no matching footnote
+  --> tests/ui/doc_suspicious_footnotes.rs:76:38
+   |
+LL |         doc = "This is not a footnote[^1].\n\nThis is not a footnote[^either], but it doesn't warn."
+   |                                      ^^^^
+   |
+help: add footnote definition
+   |
+LL ~         doc = r#"This is not a footnote[^1].
+LL + 
+LL + This is not a footnote[^either], but it doesn't warn.
+LL + 
+LL + [^1]: <!-- description -->"#
+   |
+
+error: looks like a footnote ref, but has no matching footnote
+  --> tests/ui/doc_suspicious_footnotes.rs:91:9
+   |
+LL | #[doc = r"This is not a footnote[^1]."]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: add footnote definition
+   |
+LL ~ #[doc = r#"This is not a footnote[^1].
+LL + 
+LL ~ [^1]: <!-- description -->"#]
+   |
+
+error: looks like a footnote ref, but has no matching footnote
+  --> tests/ui/doc_suspicious_footnotes.rs:111:30
+   |
+LL |      * This is not a footnote[^1].
+   |                              ^^^^
+   |
+help: add footnote definition
+   |
+LL ~      */
+LL + /*! [^1]: <!-- description --> */
+   |
+
+error: looks like a footnote ref, but has no matching footnote
+  --> tests/ui/doc_suspicious_footnotes.rs:122:30
+   |
+LL |      * This is not a footnote[^1].
+   |                              ^^^^
+   |
+help: add footnote definition
+   |
+LL ~      */
+LL + /** [^1]: <!-- description --> */
+   |
+
+error: looks like a footnote ref, but has no matching footnote
+  --> tests/ui/doc_suspicious_footnotes.rs:135:28
+   |
+LL | /// This is not a footnote [^1]
+   |                            ^^^^
+   |
+help: add footnote definition
+   |
+LL ~ /// This is not a footnote [^1]
+LL + ///
+LL + /// [^1]: <!-- description -->
+   |
+
+error: looks like a footnote ref, but has no matching footnote
+  --> tests/ui/doc_suspicious_footnotes.rs:141:33
+   |
+LL | #[doc = "This is not a footnote [^3]"]
+   |                                 ^^^^
+   |
+help: add footnote definition
+   |
+LL ~ #[doc = r#"This is not a footnote [^3]
+LL + 
+LL ~ [^3]: <!-- description -->"#]
+   |
+
+error: looks like a footnote ref, but has no matching footnote
+  --> tests/ui/doc_suspicious_footnotes.rs:150:28
+   |
+LL | /// This is not a footnote [^5]
+   |                            ^^^^
+   |
+help: add footnote definition
+   |
+LL ~ /// This is not a footnote [^5]
+LL + ///
+LL + /// [^5]: <!-- description -->
+   |
+
+error: looks like a footnote ref, but has no matching footnote
+  --> tests/ui/doc_suspicious_footnotes.rs:156:33
+   |
+LL | #[doc = "This is not a footnote [^7]"]
+   |                                 ^^^^
+   |
+help: add footnote definition
+   |
+LL ~ #[doc = r#"This is not a footnote [^7]
+LL + 
+LL ~ [^7]: <!-- description -->"#]
+   |
+
+error: aborting due to 12 previous errors
+
diff --git a/src/tools/clippy/tests/ui/doc_suspicious_footnotes_include.rs b/src/tools/clippy/tests/ui/doc_suspicious_footnotes_include.rs
new file mode 100644
index 00000000000..4f75ad94eaf
--- /dev/null
+++ b/src/tools/clippy/tests/ui/doc_suspicious_footnotes_include.rs
@@ -0,0 +1,4 @@
+//@ error-in-other-file: footnote
+//@ no-rustfix
+#![warn(clippy::doc_suspicious_footnotes)]
+#![doc=include_str!("doc_suspicious_footnotes_include.txt")]
diff --git a/src/tools/clippy/tests/ui/doc_suspicious_footnotes_include.stderr b/src/tools/clippy/tests/ui/doc_suspicious_footnotes_include.stderr
new file mode 100644
index 00000000000..74154e3f4ef
--- /dev/null
+++ b/src/tools/clippy/tests/ui/doc_suspicious_footnotes_include.stderr
@@ -0,0 +1,17 @@
+error: looks like a footnote ref, but has no matching footnote
+  --> tests/ui/doc_suspicious_footnotes_include.txt:1:23
+   |
+LL | This is not a footnote[^1].
+   |                       ^^^^
+   |
+   = note: `-D clippy::doc-suspicious-footnotes` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::doc_suspicious_footnotes)]`
+help: add footnote definition
+   |
+LL ~ [^2]: hello world
+LL + 
+LL + [^1]: <!-- description -->
+   |
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/clippy/tests/ui/doc_suspicious_footnotes_include.txt b/src/tools/clippy/tests/ui/doc_suspicious_footnotes_include.txt
new file mode 100644
index 00000000000..2a533e32c4a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/doc_suspicious_footnotes_include.txt
@@ -0,0 +1,13 @@
+This is not a footnote[^1]. //~ doc_suspicious_footnotes
+
+This is not a footnote[^either], but it doesn't warn.
+
+This is not a footnote\[^1], but it also doesn't warn.
+
+This is not a footnote[^1\], but it also doesn't warn.
+
+This is not a `footnote[^1]`, but it also doesn't warn.
+
+This is a footnote[^2].
+
+[^2]: hello world
diff --git a/src/tools/clippy/tests/ui/format_args.fixed b/src/tools/clippy/tests/ui/format_args.fixed
index edfdaa23ca1..3cb6c0b4d97 100644
--- a/src/tools/clippy/tests/ui/format_args.fixed
+++ b/src/tools/clippy/tests/ui/format_args.fixed
@@ -192,3 +192,13 @@ mod issue_9256 {
         print_substring("Hello, world!");
     }
 }
+
+mod issue14952 {
+    use std::path::Path;
+    struct Foo(Path);
+    impl std::fmt::Debug for Foo {
+        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+            write!(f, "{:?}", &self.0)
+        }
+    }
+}
diff --git a/src/tools/clippy/tests/ui/format_args.rs b/src/tools/clippy/tests/ui/format_args.rs
index 367560d577d..8a9c369fff3 100644
--- a/src/tools/clippy/tests/ui/format_args.rs
+++ b/src/tools/clippy/tests/ui/format_args.rs
@@ -192,3 +192,13 @@ mod issue_9256 {
         print_substring("Hello, world!");
     }
 }
+
+mod issue14952 {
+    use std::path::Path;
+    struct Foo(Path);
+    impl std::fmt::Debug for Foo {
+        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+            write!(f, "{:?}", &self.0)
+        }
+    }
+}
diff --git a/src/tools/clippy/tests/ui/indexing_slicing_index.rs b/src/tools/clippy/tests/ui/indexing_slicing_index.rs
index ab6a8235008..2510a023acd 100644
--- a/src/tools/clippy/tests/ui/indexing_slicing_index.rs
+++ b/src/tools/clippy/tests/ui/indexing_slicing_index.rs
@@ -68,7 +68,6 @@ fn main() {
     // This should be linted, since `suppress-restriction-lint-in-const` default is false.
     const { &ARR[idx4()] };
     //~^ ERROR: indexing may panic
-    //~| ERROR: index out of bounds
 
     let y = &x;
     // Ok, referencing shouldn't affect this lint. See the issue 6021
diff --git a/src/tools/clippy/tests/ui/indexing_slicing_index.stderr b/src/tools/clippy/tests/ui/indexing_slicing_index.stderr
index 8e24b898ed5..c68e1d53a93 100644
--- a/src/tools/clippy/tests/ui/indexing_slicing_index.stderr
+++ b/src/tools/clippy/tests/ui/indexing_slicing_index.stderr
@@ -9,18 +9,6 @@ LL | const REF: &i32 = &ARR[idx()]; // This should be linted, since `suppress-re
    = note: `-D clippy::indexing-slicing` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::indexing_slicing)]`
 
-error[E0080]: index out of bounds: the length is 2 but the index is 4
-  --> tests/ui/indexing_slicing_index.rs:69:14
-   |
-LL |     const { &ARR[idx4()] };
-   |              ^^^^^^^^^^^ evaluation of `main::{constant#3}` failed here
-
-note: erroneous constant encountered
-  --> tests/ui/indexing_slicing_index.rs:69:5
-   |
-LL |     const { &ARR[idx4()] };
-   |     ^^^^^^^^^^^^^^^^^^^^^^
-
 error: indexing may panic
   --> tests/ui/indexing_slicing_index.rs:48:5
    |
@@ -63,13 +51,13 @@ LL |     const { &ARR[idx4()] };
    = note: the suggestion might not be applicable in constant blocks
 
 error: index is out of bounds
-  --> tests/ui/indexing_slicing_index.rs:77:5
+  --> tests/ui/indexing_slicing_index.rs:76:5
    |
 LL |     y[4];
    |     ^^^^
 
 error: indexing may panic
-  --> tests/ui/indexing_slicing_index.rs:81:5
+  --> tests/ui/indexing_slicing_index.rs:80:5
    |
 LL |     v[0];
    |     ^^^^
@@ -77,7 +65,7 @@ LL |     v[0];
    = help: consider using `.get(n)` or `.get_mut(n)` instead
 
 error: indexing may panic
-  --> tests/ui/indexing_slicing_index.rs:83:5
+  --> tests/ui/indexing_slicing_index.rs:82:5
    |
 LL |     v[10];
    |     ^^^^^
@@ -85,7 +73,7 @@ LL |     v[10];
    = help: consider using `.get(n)` or `.get_mut(n)` instead
 
 error: indexing may panic
-  --> tests/ui/indexing_slicing_index.rs:85:5
+  --> tests/ui/indexing_slicing_index.rs:84:5
    |
 LL |     v[1 << 3];
    |     ^^^^^^^^^
@@ -93,13 +81,13 @@ LL |     v[1 << 3];
    = help: consider using `.get(n)` or `.get_mut(n)` instead
 
 error: index is out of bounds
-  --> tests/ui/indexing_slicing_index.rs:93:5
+  --> tests/ui/indexing_slicing_index.rs:92:5
    |
 LL |     x[N];
    |     ^^^^
 
 error: indexing may panic
-  --> tests/ui/indexing_slicing_index.rs:97:5
+  --> tests/ui/indexing_slicing_index.rs:96:5
    |
 LL |     v[N];
    |     ^^^^
@@ -107,7 +95,7 @@ LL |     v[N];
    = help: consider using `.get(n)` or `.get_mut(n)` instead
 
 error: indexing may panic
-  --> tests/ui/indexing_slicing_index.rs:99:5
+  --> tests/ui/indexing_slicing_index.rs:98:5
    |
 LL |     v[M];
    |     ^^^^
@@ -115,11 +103,10 @@ LL |     v[M];
    = help: consider using `.get(n)` or `.get_mut(n)` instead
 
 error: index is out of bounds
-  --> tests/ui/indexing_slicing_index.rs:103:13
+  --> tests/ui/indexing_slicing_index.rs:102:13
    |
 LL |     let _ = x[4];
    |             ^^^^
 
-error: aborting due to 15 previous errors
+error: aborting due to 14 previous errors
 
-For more information about this error, try `rustc --explain E0080`.
diff --git a/src/tools/clippy/tests/ui/infallible_try_from.rs b/src/tools/clippy/tests/ui/infallible_try_from.rs
new file mode 100644
index 00000000000..6a1f12f824f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/infallible_try_from.rs
@@ -0,0 +1,33 @@
+#![feature(never_type)]
+#![warn(clippy::infallible_try_from)]
+
+use std::convert::Infallible;
+
+struct MyStruct(i32);
+
+impl TryFrom<i8> for MyStruct {
+    //~^ infallible_try_from
+    type Error = !;
+    fn try_from(other: i8) -> Result<Self, !> {
+        Ok(Self(other.into()))
+    }
+}
+
+impl TryFrom<i16> for MyStruct {
+    //~^ infallible_try_from
+    type Error = Infallible;
+    fn try_from(other: i16) -> Result<Self, Infallible> {
+        Ok(Self(other.into()))
+    }
+}
+
+impl TryFrom<i64> for MyStruct {
+    type Error = i64;
+    fn try_from(other: i64) -> Result<Self, i64> {
+        Ok(Self(i32::try_from(other).map_err(|_| other)?))
+    }
+}
+
+fn main() {
+    // test code goes here
+}
diff --git a/src/tools/clippy/tests/ui/infallible_try_from.stderr b/src/tools/clippy/tests/ui/infallible_try_from.stderr
new file mode 100644
index 00000000000..705b1188489
--- /dev/null
+++ b/src/tools/clippy/tests/ui/infallible_try_from.stderr
@@ -0,0 +1,23 @@
+error: infallible TryFrom impl; consider implementing From, instead
+  --> tests/ui/infallible_try_from.rs:8:1
+   |
+LL | impl TryFrom<i8> for MyStruct {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |
+LL |     type Error = !;
+   |                  - infallible error type
+   |
+   = note: `-D clippy::infallible-try-from` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::infallible_try_from)]`
+
+error: infallible TryFrom impl; consider implementing From, instead
+  --> tests/ui/infallible_try_from.rs:16:1
+   |
+LL | impl TryFrom<i16> for MyStruct {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |
+LL |     type Error = Infallible;
+   |                  ---------- infallible error type
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/ip_constant.fixed b/src/tools/clippy/tests/ui/ip_constant.fixed
new file mode 100644
index 00000000000..2e3389c1193
--- /dev/null
+++ b/src/tools/clippy/tests/ui/ip_constant.fixed
@@ -0,0 +1,107 @@
+#![warn(clippy::ip_constant)]
+#![allow(dead_code)]
+#![allow(clippy::identity_op)]
+#![allow(clippy::eq_op)]
+
+fn literal_test1() {
+    use std::net::Ipv4Addr;
+    let _ = Ipv4Addr::LOCALHOST;
+    //~^ ip_constant
+    let _ = Ipv4Addr::BROADCAST;
+    //~^ ip_constant
+    let _ = Ipv4Addr::UNSPECIFIED;
+    //~^ ip_constant
+
+    use std::net::Ipv6Addr;
+    let _ = Ipv6Addr::LOCALHOST;
+    //~^ ip_constant
+    let _ = Ipv6Addr::UNSPECIFIED;
+    //~^ ip_constant
+}
+
+fn literal_test2() {
+    use std::net;
+    let _ = net::Ipv4Addr::LOCALHOST;
+    //~^ ip_constant
+    let _ = net::Ipv4Addr::BROADCAST;
+    //~^ ip_constant
+    let _ = net::Ipv4Addr::UNSPECIFIED;
+    //~^ ip_constant
+
+    let _ = net::Ipv6Addr::LOCALHOST;
+    //~^ ip_constant
+    let _ = net::Ipv6Addr::UNSPECIFIED;
+    //~^ ip_constant
+}
+
+fn literal_test3() {
+    let _ = std::net::Ipv4Addr::LOCALHOST;
+    //~^ ip_constant
+    let _ = std::net::Ipv4Addr::BROADCAST;
+    //~^ ip_constant
+    let _ = std::net::Ipv4Addr::UNSPECIFIED;
+    //~^ ip_constant
+
+    let _ = std::net::Ipv6Addr::LOCALHOST;
+    //~^ ip_constant
+    let _ = std::net::Ipv6Addr::UNSPECIFIED;
+    //~^ ip_constant
+}
+
+const CONST_U8_0: u8 = 0;
+const CONST_U8_1: u8 = 1;
+const CONST_U8_127: u8 = 127;
+const CONST_U8_255: u8 = 255;
+
+const CONST_U16_0: u16 = 0;
+const CONST_U16_1: u16 = 1;
+
+fn const_test1() {
+    use std::net::Ipv4Addr;
+    let _ = Ipv4Addr::LOCALHOST;
+    //~^ ip_constant
+    let _ = Ipv4Addr::BROADCAST;
+    //~^ ip_constant
+    let _ = Ipv4Addr::UNSPECIFIED;
+    //~^ ip_constant
+
+    use std::net::Ipv6Addr;
+    let _ = Ipv6Addr::LOCALHOST;
+
+    let _ = Ipv6Addr::UNSPECIFIED;
+}
+
+fn const_test2() {
+    use std::net::Ipv4Addr;
+    let _ = Ipv4Addr::LOCALHOST;
+    //~^ ip_constant
+    let _ = Ipv4Addr::BROADCAST;
+    //~^ ip_constant
+    let _ = Ipv4Addr::UNSPECIFIED;
+    //~^ ip_constant
+
+    use std::net::Ipv6Addr;
+    let _ = Ipv6Addr::LOCALHOST;
+    //~^ ip_constant
+    let _ = Ipv6Addr::LOCALHOST;
+    //~^ ip_constant
+}
+
+macro_rules! ipv4_new {
+    ($a:expr, $b:expr, $c:expr, $d:expr) => {
+        std::net::Ipv4Addr::new($a, $b, $c, $d)
+    };
+}
+
+fn macro_test() {
+    let _ = ipv4_new!(127, 0, 0, 1);
+    // no lint
+    let _ = ipv4_new!(255, 255, 255, 255);
+    // no lint
+    let _ = ipv4_new!(0, 0, 0, 0);
+    // no lint
+}
+
+fn main() {
+    // UI Test
+}
diff --git a/src/tools/clippy/tests/ui/ip_constant.rs b/src/tools/clippy/tests/ui/ip_constant.rs
new file mode 100644
index 00000000000..15e0b0551ba
--- /dev/null
+++ b/src/tools/clippy/tests/ui/ip_constant.rs
@@ -0,0 +1,127 @@
+#![warn(clippy::ip_constant)]
+#![allow(dead_code)]
+#![allow(clippy::identity_op)]
+#![allow(clippy::eq_op)]
+
+fn literal_test1() {
+    use std::net::Ipv4Addr;
+    let _ = Ipv4Addr::new(127, 0, 0, 1);
+    //~^ ip_constant
+    let _ = Ipv4Addr::new(255, 255, 255, 255);
+    //~^ ip_constant
+    let _ = Ipv4Addr::new(0, 0, 0, 0);
+    //~^ ip_constant
+
+    use std::net::Ipv6Addr;
+    let _ = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1);
+    //~^ ip_constant
+    let _ = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0);
+    //~^ ip_constant
+}
+
+fn literal_test2() {
+    use std::net;
+    let _ = net::Ipv4Addr::new(127, 0, 0, 1);
+    //~^ ip_constant
+    let _ = net::Ipv4Addr::new(255, 255, 255, 255);
+    //~^ ip_constant
+    let _ = net::Ipv4Addr::new(0, 0, 0, 0);
+    //~^ ip_constant
+
+    let _ = net::Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1);
+    //~^ ip_constant
+    let _ = net::Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0);
+    //~^ ip_constant
+}
+
+fn literal_test3() {
+    let _ = std::net::Ipv4Addr::new(127, 0, 0, 1);
+    //~^ ip_constant
+    let _ = std::net::Ipv4Addr::new(255, 255, 255, 255);
+    //~^ ip_constant
+    let _ = std::net::Ipv4Addr::new(0, 0, 0, 0);
+    //~^ ip_constant
+
+    let _ = std::net::Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1);
+    //~^ ip_constant
+    let _ = std::net::Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0);
+    //~^ ip_constant
+}
+
+const CONST_U8_0: u8 = 0;
+const CONST_U8_1: u8 = 1;
+const CONST_U8_127: u8 = 127;
+const CONST_U8_255: u8 = 255;
+
+const CONST_U16_0: u16 = 0;
+const CONST_U16_1: u16 = 1;
+
+fn const_test1() {
+    use std::net::Ipv4Addr;
+    let _ = Ipv4Addr::new(CONST_U8_127, CONST_U8_0, CONST_U8_0, CONST_U8_1);
+    //~^ ip_constant
+    let _ = Ipv4Addr::new(CONST_U8_255, CONST_U8_255, CONST_U8_255, CONST_U8_255);
+    //~^ ip_constant
+    let _ = Ipv4Addr::new(CONST_U8_0, CONST_U8_0, CONST_U8_0, CONST_U8_0);
+    //~^ ip_constant
+
+    use std::net::Ipv6Addr;
+    let _ = Ipv6Addr::new(
+        //~^ ip_constant
+        CONST_U16_0,
+        CONST_U16_0,
+        CONST_U16_0,
+        CONST_U16_0,
+        CONST_U16_0,
+        CONST_U16_0,
+        CONST_U16_0,
+        CONST_U16_1,
+    );
+
+    let _ = Ipv6Addr::new(
+        //~^ ip_constant
+        CONST_U16_0,
+        CONST_U16_0,
+        CONST_U16_0,
+        CONST_U16_0,
+        CONST_U16_0,
+        CONST_U16_0,
+        CONST_U16_0,
+        CONST_U16_0,
+    );
+}
+
+fn const_test2() {
+    use std::net::Ipv4Addr;
+    let _ = Ipv4Addr::new(126 + 1, 0, 0, 1);
+    //~^ ip_constant
+    let _ = Ipv4Addr::new(254 + CONST_U8_1, 255, { 255 - CONST_U8_0 }, CONST_U8_255);
+    //~^ ip_constant
+    let _ = Ipv4Addr::new(0, CONST_U8_255 - 255, 0, { 1 + 0 - 1 });
+    //~^ ip_constant
+
+    use std::net::Ipv6Addr;
+    let _ = Ipv6Addr::new(0 + CONST_U16_0, 0, 0, 0, 0, 0, 0, 1);
+    //~^ ip_constant
+    let _ = Ipv6Addr::new(0 + 0, 0, 0, 0, 0, { 2 - 1 - CONST_U16_1 }, 0, 1);
+    //~^ ip_constant
+}
+
+macro_rules! ipv4_new {
+    ($a:expr, $b:expr, $c:expr, $d:expr) => {
+        std::net::Ipv4Addr::new($a, $b, $c, $d)
+    };
+}
+
+fn macro_test() {
+    let _ = ipv4_new!(127, 0, 0, 1);
+    // no lint
+    let _ = ipv4_new!(255, 255, 255, 255);
+    // no lint
+    let _ = ipv4_new!(0, 0, 0, 0);
+    // no lint
+}
+
+fn main() {
+    // UI Test
+}
diff --git a/src/tools/clippy/tests/ui/ip_constant.stderr b/src/tools/clippy/tests/ui/ip_constant.stderr
new file mode 100644
index 00000000000..3e984c6cb3b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/ip_constant.stderr
@@ -0,0 +1,338 @@
+error: hand-coded well-known IP address
+  --> tests/ui/ip_constant.rs:8:13
+   |
+LL |     let _ = Ipv4Addr::new(127, 0, 0, 1);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `-D clippy::ip-constant` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::ip_constant)]`
+help: use
+   |
+LL -     let _ = Ipv4Addr::new(127, 0, 0, 1);
+LL +     let _ = Ipv4Addr::LOCALHOST;
+   |
+
+error: hand-coded well-known IP address
+  --> tests/ui/ip_constant.rs:10:13
+   |
+LL |     let _ = Ipv4Addr::new(255, 255, 255, 255);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use
+   |
+LL -     let _ = Ipv4Addr::new(255, 255, 255, 255);
+LL +     let _ = Ipv4Addr::BROADCAST;
+   |
+
+error: hand-coded well-known IP address
+  --> tests/ui/ip_constant.rs:12:13
+   |
+LL |     let _ = Ipv4Addr::new(0, 0, 0, 0);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use
+   |
+LL -     let _ = Ipv4Addr::new(0, 0, 0, 0);
+LL +     let _ = Ipv4Addr::UNSPECIFIED;
+   |
+
+error: hand-coded well-known IP address
+  --> tests/ui/ip_constant.rs:16:13
+   |
+LL |     let _ = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use
+   |
+LL -     let _ = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1);
+LL +     let _ = Ipv6Addr::LOCALHOST;
+   |
+
+error: hand-coded well-known IP address
+  --> tests/ui/ip_constant.rs:18:13
+   |
+LL |     let _ = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use
+   |
+LL -     let _ = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0);
+LL +     let _ = Ipv6Addr::UNSPECIFIED;
+   |
+
+error: hand-coded well-known IP address
+  --> tests/ui/ip_constant.rs:24:13
+   |
+LL |     let _ = net::Ipv4Addr::new(127, 0, 0, 1);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use
+   |
+LL -     let _ = net::Ipv4Addr::new(127, 0, 0, 1);
+LL +     let _ = net::Ipv4Addr::LOCALHOST;
+   |
+
+error: hand-coded well-known IP address
+  --> tests/ui/ip_constant.rs:26:13
+   |
+LL |     let _ = net::Ipv4Addr::new(255, 255, 255, 255);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use
+   |
+LL -     let _ = net::Ipv4Addr::new(255, 255, 255, 255);
+LL +     let _ = net::Ipv4Addr::BROADCAST;
+   |
+
+error: hand-coded well-known IP address
+  --> tests/ui/ip_constant.rs:28:13
+   |
+LL |     let _ = net::Ipv4Addr::new(0, 0, 0, 0);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use
+   |
+LL -     let _ = net::Ipv4Addr::new(0, 0, 0, 0);
+LL +     let _ = net::Ipv4Addr::UNSPECIFIED;
+   |
+
+error: hand-coded well-known IP address
+  --> tests/ui/ip_constant.rs:31:13
+   |
+LL |     let _ = net::Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use
+   |
+LL -     let _ = net::Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1);
+LL +     let _ = net::Ipv6Addr::LOCALHOST;
+   |
+
+error: hand-coded well-known IP address
+  --> tests/ui/ip_constant.rs:33:13
+   |
+LL |     let _ = net::Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use
+   |
+LL -     let _ = net::Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0);
+LL +     let _ = net::Ipv6Addr::UNSPECIFIED;
+   |
+
+error: hand-coded well-known IP address
+  --> tests/ui/ip_constant.rs:38:13
+   |
+LL |     let _ = std::net::Ipv4Addr::new(127, 0, 0, 1);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use
+   |
+LL -     let _ = std::net::Ipv4Addr::new(127, 0, 0, 1);
+LL +     let _ = std::net::Ipv4Addr::LOCALHOST;
+   |
+
+error: hand-coded well-known IP address
+  --> tests/ui/ip_constant.rs:40:13
+   |
+LL |     let _ = std::net::Ipv4Addr::new(255, 255, 255, 255);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use
+   |
+LL -     let _ = std::net::Ipv4Addr::new(255, 255, 255, 255);
+LL +     let _ = std::net::Ipv4Addr::BROADCAST;
+   |
+
+error: hand-coded well-known IP address
+  --> tests/ui/ip_constant.rs:42:13
+   |
+LL |     let _ = std::net::Ipv4Addr::new(0, 0, 0, 0);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use
+   |
+LL -     let _ = std::net::Ipv4Addr::new(0, 0, 0, 0);
+LL +     let _ = std::net::Ipv4Addr::UNSPECIFIED;
+   |
+
+error: hand-coded well-known IP address
+  --> tests/ui/ip_constant.rs:45:13
+   |
+LL |     let _ = std::net::Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use
+   |
+LL -     let _ = std::net::Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1);
+LL +     let _ = std::net::Ipv6Addr::LOCALHOST;
+   |
+
+error: hand-coded well-known IP address
+  --> tests/ui/ip_constant.rs:47:13
+   |
+LL |     let _ = std::net::Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use
+   |
+LL -     let _ = std::net::Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0);
+LL +     let _ = std::net::Ipv6Addr::UNSPECIFIED;
+   |
+
+error: hand-coded well-known IP address
+  --> tests/ui/ip_constant.rs:61:13
+   |
+LL |     let _ = Ipv4Addr::new(CONST_U8_127, CONST_U8_0, CONST_U8_0, CONST_U8_1);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use
+   |
+LL -     let _ = Ipv4Addr::new(CONST_U8_127, CONST_U8_0, CONST_U8_0, CONST_U8_1);
+LL +     let _ = Ipv4Addr::LOCALHOST;
+   |
+
+error: hand-coded well-known IP address
+  --> tests/ui/ip_constant.rs:63:13
+   |
+LL |     let _ = Ipv4Addr::new(CONST_U8_255, CONST_U8_255, CONST_U8_255, CONST_U8_255);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use
+   |
+LL -     let _ = Ipv4Addr::new(CONST_U8_255, CONST_U8_255, CONST_U8_255, CONST_U8_255);
+LL +     let _ = Ipv4Addr::BROADCAST;
+   |
+
+error: hand-coded well-known IP address
+  --> tests/ui/ip_constant.rs:65:13
+   |
+LL |     let _ = Ipv4Addr::new(CONST_U8_0, CONST_U8_0, CONST_U8_0, CONST_U8_0);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use
+   |
+LL -     let _ = Ipv4Addr::new(CONST_U8_0, CONST_U8_0, CONST_U8_0, CONST_U8_0);
+LL +     let _ = Ipv4Addr::UNSPECIFIED;
+   |
+
+error: hand-coded well-known IP address
+  --> tests/ui/ip_constant.rs:69:13
+   |
+LL |       let _ = Ipv6Addr::new(
+   |  _____________^
+LL | |
+LL | |         CONST_U16_0,
+LL | |         CONST_U16_0,
+...  |
+LL | |         CONST_U16_1,
+LL | |     );
+   | |_____^
+   |
+help: use
+   |
+LL -     let _ = Ipv6Addr::new(
+LL -
+LL -         CONST_U16_0,
+LL -         CONST_U16_0,
+LL -         CONST_U16_0,
+LL -         CONST_U16_0,
+LL -         CONST_U16_0,
+LL -         CONST_U16_0,
+LL -         CONST_U16_0,
+LL -         CONST_U16_1,
+LL -     );
+LL +     let _ = Ipv6Addr::LOCALHOST;
+   |
+
+error: hand-coded well-known IP address
+  --> tests/ui/ip_constant.rs:81:13
+   |
+LL |       let _ = Ipv6Addr::new(
+   |  _____________^
+LL | |
+LL | |         CONST_U16_0,
+LL | |         CONST_U16_0,
+...  |
+LL | |         CONST_U16_0,
+LL | |     );
+   | |_____^
+   |
+help: use
+   |
+LL -     let _ = Ipv6Addr::new(
+LL -
+LL -         CONST_U16_0,
+LL -         CONST_U16_0,
+LL -         CONST_U16_0,
+LL -         CONST_U16_0,
+LL -         CONST_U16_0,
+LL -         CONST_U16_0,
+LL -         CONST_U16_0,
+LL -         CONST_U16_0,
+LL -     );
+LL +     let _ = Ipv6Addr::UNSPECIFIED;
+   |
+
+error: hand-coded well-known IP address
+  --> tests/ui/ip_constant.rs:96:13
+   |
+LL |     let _ = Ipv4Addr::new(126 + 1, 0, 0, 1);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use
+   |
+LL -     let _ = Ipv4Addr::new(126 + 1, 0, 0, 1);
+LL +     let _ = Ipv4Addr::LOCALHOST;
+   |
+
+error: hand-coded well-known IP address
+  --> tests/ui/ip_constant.rs:98:13
+   |
+LL |     let _ = Ipv4Addr::new(254 + CONST_U8_1, 255, { 255 - CONST_U8_0 }, CONST_U8_255);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use
+   |
+LL -     let _ = Ipv4Addr::new(254 + CONST_U8_1, 255, { 255 - CONST_U8_0 }, CONST_U8_255);
+LL +     let _ = Ipv4Addr::BROADCAST;
+   |
+
+error: hand-coded well-known IP address
+  --> tests/ui/ip_constant.rs:100:13
+   |
+LL |     let _ = Ipv4Addr::new(0, CONST_U8_255 - 255, 0, { 1 + 0 - 1 });
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use
+   |
+LL -     let _ = Ipv4Addr::new(0, CONST_U8_255 - 255, 0, { 1 + 0 - 1 });
+LL +     let _ = Ipv4Addr::UNSPECIFIED;
+   |
+
+error: hand-coded well-known IP address
+  --> tests/ui/ip_constant.rs:104:13
+   |
+LL |     let _ = Ipv6Addr::new(0 + CONST_U16_0, 0, 0, 0, 0, 0, 0, 1);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use
+   |
+LL -     let _ = Ipv6Addr::new(0 + CONST_U16_0, 0, 0, 0, 0, 0, 0, 1);
+LL +     let _ = Ipv6Addr::LOCALHOST;
+   |
+
+error: hand-coded well-known IP address
+  --> tests/ui/ip_constant.rs:106:13
+   |
+LL |     let _ = Ipv6Addr::new(0 + 0, 0, 0, 0, 0, { 2 - 1 - CONST_U16_1 }, 0, 1);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use
+   |
+LL -     let _ = Ipv6Addr::new(0 + 0, 0, 0, 0, 0, { 2 - 1 - CONST_U16_1 }, 0, 1);
+LL +     let _ = Ipv6Addr::LOCALHOST;
+   |
+
+error: aborting due to 25 previous errors
+
diff --git a/src/tools/clippy/tests/ui/ip_constant_from_external.rs b/src/tools/clippy/tests/ui/ip_constant_from_external.rs
new file mode 100644
index 00000000000..7fd27022f12
--- /dev/null
+++ b/src/tools/clippy/tests/ui/ip_constant_from_external.rs
@@ -0,0 +1,12 @@
+//@error-in-other-file: hand-coded well-known IP address
+//@no-rustfix
+#![warn(clippy::ip_constant)]
+
+fn external_constant_test() {
+    let _ = include!("localhost.txt");
+    // lint in external file `localhost.txt`
+}
+
+fn main() {
+    external_constant_test();
+}
diff --git a/src/tools/clippy/tests/ui/ip_constant_from_external.stderr b/src/tools/clippy/tests/ui/ip_constant_from_external.stderr
new file mode 100644
index 00000000000..99dd827d41f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/ip_constant_from_external.stderr
@@ -0,0 +1,16 @@
+error: hand-coded well-known IP address
+  --> tests/ui/localhost.txt:1:1
+   |
+LL | std::net::Ipv4Addr::new(127, 0, 0, 1)
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `-D clippy::ip-constant` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::ip_constant)]`
+help: use
+   |
+LL - std::net::Ipv4Addr::new(127, 0, 0, 1)
+LL + std::net::Ipv4Addr::LOCALHOST
+   |
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/clippy/tests/ui/localhost.txt b/src/tools/clippy/tests/ui/localhost.txt
new file mode 100644
index 00000000000..4502ec2b234
--- /dev/null
+++ b/src/tools/clippy/tests/ui/localhost.txt
@@ -0,0 +1 @@
+std::net::Ipv4Addr::new(127, 0, 0, 1)
\ No newline at end of file
diff --git a/src/tools/clippy/tests/ui/manual_flatten.fixed b/src/tools/clippy/tests/ui/manual_flatten.fixed
new file mode 100644
index 00000000000..cc1fbd25765
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_flatten.fixed
@@ -0,0 +1,148 @@
+#![warn(clippy::manual_flatten)]
+#![allow(clippy::useless_vec, clippy::uninlined_format_args)]
+
+fn main() {
+    // Test for loop over implicitly adjusted `Iterator` with `if let` expression
+    let x = vec![Some(1), Some(2), Some(3)];
+    for y in x.into_iter().flatten() {
+        println!("{}", y);
+    }
+
+    // Test for loop over implicitly adjusted `Iterator` with `if let` statement
+    let y: Vec<Result<i32, i32>> = vec![];
+    for n in y.clone().into_iter().flatten() {
+        println!("{}", n);
+    }
+
+    // Test for loop over by reference
+    for n in y.iter().flatten() {
+        println!("{}", n);
+    }
+
+    // Test for loop over an implicit reference
+    let z = &y;
+    for n in z.iter().flatten() {
+        println!("{}", n);
+    }
+
+    // Test for loop over `Iterator` with `if let` expression
+    let z = vec![Some(1), Some(2), Some(3)];
+    let z = z.iter();
+    for m in z.flatten() {
+        println!("{}", m);
+    }
+
+    // Using the `None` variant should not trigger the lint
+    // Note: for an autofixable suggestion, the binding in the for loop has to take the
+    // name of the binding in the `if let`
+    let z = vec![Some(1), Some(2), Some(3)];
+    for n in z {
+        if n.is_none() {
+            println!("Nada.");
+        }
+    }
+
+    // Using the `Err` variant should not trigger the lint
+    for n in y.clone() {
+        if let Err(e) = n {
+            println!("Oops: {}!", e);
+        }
+    }
+
+    // Having an else clause should not trigger the lint
+    for n in y.clone() {
+        if let Ok(n) = n {
+            println!("{}", n);
+        } else {
+            println!("Oops!");
+        }
+    }
+
+    let vec_of_ref = vec![&Some(1)];
+    for n in vec_of_ref.iter().copied().flatten() {
+        println!("{:?}", n);
+    }
+
+    let vec_of_ref = &vec_of_ref;
+    for n in vec_of_ref.iter().copied().flatten() {
+        println!("{:?}", n);
+    }
+
+    let slice_of_ref = &[&Some(1)];
+    for n in slice_of_ref.iter().copied().flatten() {
+        println!("{:?}", n);
+    }
+
+    struct Test {
+        a: usize,
+    }
+
+    let mut vec_of_struct = [Some(Test { a: 1 }), None];
+
+    // Usage of `if let` expression should not trigger lint
+    for n in vec_of_struct.iter_mut() {
+        if let Some(z) = n {
+            *n = None;
+        }
+    }
+
+    // Using manual flatten should not trigger the lint
+    for n in vec![Some(1), Some(2), Some(3)].iter().flatten() {
+        println!("{}", n);
+    }
+
+    // Using nested `Some` pattern should not trigger the lint
+    for n in vec![Some((1, Some(2)))] {
+        if let Some((_, Some(n))) = n {
+            println!("{}", n);
+        }
+    }
+
+    macro_rules! inner {
+        ($id:ident / $new:pat => $action:expr) => {
+            if let Some($new) = $id {
+                $action;
+            }
+        };
+    }
+
+    // Usage of `if let` expression with macro should not trigger lint
+    for ab in [Some((1, 2)), Some((3, 4))] {
+        inner!(ab / (c, d) => println!("{c}-{d}"));
+    }
+
+    macro_rules! args {
+        ($($arg:expr),*) => {
+            vec![$(Some($arg)),*]
+        };
+    }
+
+    // Usage of `if let` expression with macro should not trigger lint
+    for n in args!(1, 2, 3) {
+        if let Some(n) = n {
+            println!("{:?}", n);
+        }
+    }
+
+    // This should trigger the lint, but the applicability is `MaybeIncorrect`
+    let z = vec![Some(1), Some(2), Some(3)];
+    for n in z.into_iter().flatten() {
+        println!("{:?}", n);
+    }
+
+    run_unformatted_tests();
+}
+
+#[rustfmt::skip]
+fn run_unformatted_tests() {
+    // Skip rustfmt here on purpose so the suggestion does not fit in one line
+    for n in vec![
+    //~^ manual_flatten
+
+        Some(1),
+        Some(2),
+        Some(3)
+    ].iter().flatten() {
+        println!("{:?}", n);
+    }
+}
diff --git a/src/tools/clippy/tests/ui/manual_flatten.rs b/src/tools/clippy/tests/ui/manual_flatten.rs
index f1a0053ef38..53b4ac7d3b6 100644
--- a/src/tools/clippy/tests/ui/manual_flatten.rs
+++ b/src/tools/clippy/tests/ui/manual_flatten.rs
@@ -1,6 +1,6 @@
 #![warn(clippy::manual_flatten)]
 #![allow(clippy::useless_vec, clippy::uninlined_format_args)]
-//@no-rustfix
+
 fn main() {
     // Test for loop over implicitly adjusted `Iterator` with `if let` expression
     let x = vec![Some(1), Some(2), Some(3)];
@@ -130,6 +130,43 @@ fn main() {
         }
     }
 
+    macro_rules! inner {
+        ($id:ident / $new:pat => $action:expr) => {
+            if let Some($new) = $id {
+                $action;
+            }
+        };
+    }
+
+    // Usage of `if let` expression with macro should not trigger lint
+    for ab in [Some((1, 2)), Some((3, 4))] {
+        inner!(ab / (c, d) => println!("{c}-{d}"));
+    }
+
+    macro_rules! args {
+        ($($arg:expr),*) => {
+            vec![$(Some($arg)),*]
+        };
+    }
+
+    // Usage of `if let` expression with macro should not trigger lint
+    for n in args!(1, 2, 3) {
+        if let Some(n) = n {
+            println!("{:?}", n);
+        }
+    }
+
+    // This should trigger the lint, but the applicability is `MaybeIncorrect`
+    let z = vec![Some(1), Some(2), Some(3)];
+    for n in z {
+        //~^ manual_flatten
+
+        if let Some(n) = n {
+            println!("{:?}", n);
+        }
+        // foo
+    }
+
     run_unformatted_tests();
 }
 
diff --git a/src/tools/clippy/tests/ui/manual_flatten.stderr b/src/tools/clippy/tests/ui/manual_flatten.stderr
index 9a846fe17f3..eb39ee42071 100644
--- a/src/tools/clippy/tests/ui/manual_flatten.stderr
+++ b/src/tools/clippy/tests/ui/manual_flatten.stderr
@@ -1,10 +1,7 @@
 error: unnecessary `if let` since only the `Some` variant of the iterator element is used
   --> tests/ui/manual_flatten.rs:7:5
    |
-LL |       for n in x {
-   |       ^        - help: try: `x.into_iter().flatten()`
-   |  _____|
-   | |
+LL | /     for n in x {
 LL | |
 LL | |
 LL | |         if let Some(y) = n {
@@ -12,7 +9,7 @@ LL | |         if let Some(y) = n {
 LL | |     }
    | |_____^
    |
-help: ...and remove the `if let` statement in the for loop
+help: try `.flatten()` and remove the `if let` statement in the for loop
   --> tests/ui/manual_flatten.rs:10:9
    |
 LL | /         if let Some(y) = n {
@@ -21,14 +18,17 @@ LL | |         }
    | |_________^
    = note: `-D clippy::manual-flatten` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::manual_flatten)]`
+help: try
+   |
+LL ~     for y in x.into_iter().flatten() {
+LL +         println!("{}", y);
+LL +     }
+   |
 
 error: unnecessary `if let` since only the `Ok` variant of the iterator element is used
   --> tests/ui/manual_flatten.rs:17:5
    |
-LL |       for n in y.clone() {
-   |       ^        --------- help: try: `y.clone().into_iter().flatten()`
-   |  _____|
-   | |
+LL | /     for n in y.clone() {
 LL | |
 LL | |
 LL | |         if let Ok(n) = n {
@@ -37,21 +37,24 @@ LL | |         };
 LL | |     }
    | |_____^
    |
-help: ...and remove the `if let` statement in the for loop
+help: try `.flatten()` and remove the `if let` statement in the for loop
   --> tests/ui/manual_flatten.rs:20:9
    |
 LL | /         if let Ok(n) = n {
 LL | |             println!("{}", n);
 LL | |         };
    | |_________^
+help: try
+   |
+LL ~     for n in y.clone().into_iter().flatten() {
+LL +         println!("{}", n);
+LL +     }
+   |
 
 error: unnecessary `if let` since only the `Ok` variant of the iterator element is used
   --> tests/ui/manual_flatten.rs:26:5
    |
-LL |       for n in &y {
-   |       ^        -- help: try: `y.iter().flatten()`
-   |  _____|
-   | |
+LL | /     for n in &y {
 LL | |
 LL | |
 LL | |         if let Ok(n) = n {
@@ -59,21 +62,24 @@ LL | |         if let Ok(n) = n {
 LL | |     }
    | |_____^
    |
-help: ...and remove the `if let` statement in the for loop
+help: try `.flatten()` and remove the `if let` statement in the for loop
   --> tests/ui/manual_flatten.rs:29:9
    |
 LL | /         if let Ok(n) = n {
 LL | |             println!("{}", n);
 LL | |         }
    | |_________^
+help: try
+   |
+LL ~     for n in y.iter().flatten() {
+LL +         println!("{}", n);
+LL +     }
+   |
 
 error: unnecessary `if let` since only the `Ok` variant of the iterator element is used
   --> tests/ui/manual_flatten.rs:36:5
    |
-LL |       for n in z {
-   |       ^        - help: try: `z.iter().flatten()`
-   |  _____|
-   | |
+LL | /     for n in z {
 LL | |
 LL | |
 LL | |         if let Ok(n) = n {
@@ -81,21 +87,24 @@ LL | |         if let Ok(n) = n {
 LL | |     }
    | |_____^
    |
-help: ...and remove the `if let` statement in the for loop
+help: try `.flatten()` and remove the `if let` statement in the for loop
   --> tests/ui/manual_flatten.rs:39:9
    |
 LL | /         if let Ok(n) = n {
 LL | |             println!("{}", n);
 LL | |         }
    | |_________^
+help: try
+   |
+LL ~     for n in z.iter().flatten() {
+LL +         println!("{}", n);
+LL +     }
+   |
 
 error: unnecessary `if let` since only the `Some` variant of the iterator element is used
   --> tests/ui/manual_flatten.rs:47:5
    |
-LL |       for n in z {
-   |       ^        - help: try: `z.flatten()`
-   |  _____|
-   | |
+LL | /     for n in z {
 LL | |
 LL | |
 LL | |         if let Some(m) = n {
@@ -103,21 +112,24 @@ LL | |         if let Some(m) = n {
 LL | |     }
    | |_____^
    |
-help: ...and remove the `if let` statement in the for loop
+help: try `.flatten()` and remove the `if let` statement in the for loop
   --> tests/ui/manual_flatten.rs:50:9
    |
 LL | /         if let Some(m) = n {
 LL | |             println!("{}", m);
 LL | |         }
    | |_________^
+help: try
+   |
+LL ~     for m in z.flatten() {
+LL +         println!("{}", m);
+LL +     }
+   |
 
 error: unnecessary `if let` since only the `Some` variant of the iterator element is used
   --> tests/ui/manual_flatten.rs:82:5
    |
-LL |       for n in &vec_of_ref {
-   |       ^        ----------- help: try: `vec_of_ref.iter().copied().flatten()`
-   |  _____|
-   | |
+LL | /     for n in &vec_of_ref {
 LL | |
 LL | |
 LL | |         if let Some(n) = n {
@@ -125,21 +137,24 @@ LL | |         if let Some(n) = n {
 LL | |     }
    | |_____^
    |
-help: ...and remove the `if let` statement in the for loop
+help: try `.flatten()` and remove the `if let` statement in the for loop
   --> tests/ui/manual_flatten.rs:85:9
    |
 LL | /         if let Some(n) = n {
 LL | |             println!("{:?}", n);
 LL | |         }
    | |_________^
+help: try
+   |
+LL ~     for n in vec_of_ref.iter().copied().flatten() {
+LL +         println!("{:?}", n);
+LL +     }
+   |
 
 error: unnecessary `if let` since only the `Some` variant of the iterator element is used
   --> tests/ui/manual_flatten.rs:91:5
    |
-LL |       for n in vec_of_ref {
-   |       ^        ---------- help: try: `vec_of_ref.iter().copied().flatten()`
-   |  _____|
-   | |
+LL | /     for n in vec_of_ref {
 LL | |
 LL | |
 LL | |         if let Some(n) = n {
@@ -147,21 +162,24 @@ LL | |         if let Some(n) = n {
 LL | |     }
    | |_____^
    |
-help: ...and remove the `if let` statement in the for loop
+help: try `.flatten()` and remove the `if let` statement in the for loop
   --> tests/ui/manual_flatten.rs:94:9
    |
 LL | /         if let Some(n) = n {
 LL | |             println!("{:?}", n);
 LL | |         }
    | |_________^
+help: try
+   |
+LL ~     for n in vec_of_ref.iter().copied().flatten() {
+LL +         println!("{:?}", n);
+LL +     }
+   |
 
 error: unnecessary `if let` since only the `Some` variant of the iterator element is used
   --> tests/ui/manual_flatten.rs:100:5
    |
-LL |       for n in slice_of_ref {
-   |       ^        ------------ help: try: `slice_of_ref.iter().copied().flatten()`
-   |  _____|
-   | |
+LL | /     for n in slice_of_ref {
 LL | |
 LL | |
 LL | |         if let Some(n) = n {
@@ -169,16 +187,47 @@ LL | |         if let Some(n) = n {
 LL | |     }
    | |_____^
    |
-help: ...and remove the `if let` statement in the for loop
+help: try `.flatten()` and remove the `if let` statement in the for loop
   --> tests/ui/manual_flatten.rs:103:9
    |
 LL | /         if let Some(n) = n {
 LL | |             println!("{:?}", n);
 LL | |         }
    | |_________^
+help: try
+   |
+LL ~     for n in slice_of_ref.iter().copied().flatten() {
+LL +         println!("{:?}", n);
+LL +     }
+   |
+
+error: unnecessary `if let` since only the `Some` variant of the iterator element is used
+  --> tests/ui/manual_flatten.rs:161:5
+   |
+LL | /     for n in z {
+LL | |
+LL | |
+LL | |         if let Some(n) = n {
+...  |
+LL | |     }
+   | |_____^
+   |
+help: try `.flatten()` and remove the `if let` statement in the for loop
+  --> tests/ui/manual_flatten.rs:164:9
+   |
+LL | /         if let Some(n) = n {
+LL | |             println!("{:?}", n);
+LL | |         }
+   | |_________^
+help: try
+   |
+LL ~     for n in z.into_iter().flatten() {
+LL +         println!("{:?}", n);
+LL +     }
+   |
 
 error: unnecessary `if let` since only the `Some` variant of the iterator element is used
-  --> tests/ui/manual_flatten.rs:139:5
+  --> tests/ui/manual_flatten.rs:176:5
    |
 LL | /     for n in vec![
 LL | |
@@ -188,8 +237,8 @@ LL | |         Some(1),
 LL | |     }
    | |_____^
    |
-help: remove the `if let` statement in the for loop and then...
-  --> tests/ui/manual_flatten.rs:146:9
+help: try `.flatten()` and remove the `if let` statement in the for loop
+  --> tests/ui/manual_flatten.rs:183:9
    |
 LL | /         if let Some(n) = n {
 LL | |             println!("{:?}", n);
@@ -201,7 +250,9 @@ LL |     for n in vec![
 ...
 LL |         Some(3)
 LL ~     ].iter().flatten() {
+LL +         println!("{:?}", n);
+LL +     }
    |
 
-error: aborting due to 9 previous errors
+error: aborting due to 10 previous errors
 
diff --git a/src/tools/clippy/tests/ui/manual_swap_auto_fix.fixed b/src/tools/clippy/tests/ui/manual_swap_auto_fix.fixed
index 28466ff3f9b..6cd81bafce8 100644
--- a/src/tools/clippy/tests/ui/manual_swap_auto_fix.fixed
+++ b/src/tools/clippy/tests/ui/manual_swap_auto_fix.fixed
@@ -55,3 +55,14 @@ fn swap8() {
     let i2 = 1;
     v.swap(i1 + i2, i2);
 }
+
+fn issue_14931() {
+    let mut v = [1, 2, 3, 4];
+
+    let mut i1 = 0;
+    for i2 in 0..4 {
+        v.swap(i1, i2);
+
+        i1 += 2;
+    }
+}
diff --git a/src/tools/clippy/tests/ui/manual_swap_auto_fix.rs b/src/tools/clippy/tests/ui/manual_swap_auto_fix.rs
index c9880e651cd..19dabfd833f 100644
--- a/src/tools/clippy/tests/ui/manual_swap_auto_fix.rs
+++ b/src/tools/clippy/tests/ui/manual_swap_auto_fix.rs
@@ -78,3 +78,17 @@ fn swap8() {
     v[i1 + i2] = v[i2];
     v[i2] = tmp;
 }
+
+fn issue_14931() {
+    let mut v = [1, 2, 3, 4];
+
+    let mut i1 = 0;
+    for i2 in 0..4 {
+        let tmp = v[i1];
+        //~^ manual_swap
+        v[i1] = v[i2];
+        v[i2] = tmp;
+
+        i1 += 2;
+    }
+}
diff --git a/src/tools/clippy/tests/ui/manual_swap_auto_fix.stderr b/src/tools/clippy/tests/ui/manual_swap_auto_fix.stderr
index 7ab898fcc72..a0bb32233e2 100644
--- a/src/tools/clippy/tests/ui/manual_swap_auto_fix.stderr
+++ b/src/tools/clippy/tests/ui/manual_swap_auto_fix.stderr
@@ -92,5 +92,14 @@ LL | |     v[i1 + i2] = v[i2];
 LL | |     v[i2] = tmp;
    | |________________^ help: try: `v.swap(i1 + i2, i2);`
 
-error: aborting due to 8 previous errors
+error: this looks like you are swapping elements of `v` manually
+  --> tests/ui/manual_swap_auto_fix.rs:87:9
+   |
+LL | /         let tmp = v[i1];
+LL | |
+LL | |         v[i1] = v[i2];
+LL | |         v[i2] = tmp;
+   | |____________________^ help: try: `v.swap(i1, i2);`
+
+error: aborting due to 9 previous errors
 
diff --git a/src/tools/clippy/tests/ui/match_single_binding.fixed b/src/tools/clippy/tests/ui/match_single_binding.fixed
index bdf39796ebf..e11dea35204 100644
--- a/src/tools/clippy/tests/ui/match_single_binding.fixed
+++ b/src/tools/clippy/tests/ui/match_single_binding.fixed
@@ -188,3 +188,19 @@ fn issue14634() {
     let id!(_a) = dbg!(b + 1);
     //~^^^ match_single_binding
 }
+
+mod issue14991 {
+    struct AnnoConstWOBlock {
+        inner: [(); {
+            let _n = 1;
+            42
+        }],
+    }
+
+    struct AnnoConstWBlock {
+        inner: [(); {
+            let _n = 1;
+            42
+        }],
+    }
+}
diff --git a/src/tools/clippy/tests/ui/match_single_binding.rs b/src/tools/clippy/tests/ui/match_single_binding.rs
index 419ff95d873..d498da30fc8 100644
--- a/src/tools/clippy/tests/ui/match_single_binding.rs
+++ b/src/tools/clippy/tests/ui/match_single_binding.rs
@@ -249,3 +249,21 @@ fn issue14634() {
     };
     //~^^^ match_single_binding
 }
+
+mod issue14991 {
+    struct AnnoConstWOBlock {
+        inner: [(); match 1 {
+            //~^ match_single_binding
+            _n => 42,
+        }],
+    }
+
+    struct AnnoConstWBlock {
+        inner: [(); {
+            match 1 {
+                //~^ match_single_binding
+                _n => 42,
+            }
+        }],
+    }
+}
diff --git a/src/tools/clippy/tests/ui/match_single_binding.stderr b/src/tools/clippy/tests/ui/match_single_binding.stderr
index bdd0134a5f1..f274f80c81d 100644
--- a/src/tools/clippy/tests/ui/match_single_binding.stderr
+++ b/src/tools/clippy/tests/ui/match_single_binding.stderr
@@ -378,5 +378,38 @@ LL ~     let id!(b) = dbg!(3);
 LL +     let id!(_a) = dbg!(b + 1);
    |
 
-error: aborting due to 27 previous errors
+error: this match could be written as a `let` statement
+  --> tests/ui/match_single_binding.rs:255:21
+   |
+LL |           inner: [(); match 1 {
+   |  _____________________^
+LL | |
+LL | |             _n => 42,
+LL | |         }],
+   | |_________^
+   |
+help: consider using a `let` statement
+   |
+LL ~         inner: [(); {
+LL +             let _n = 1;
+LL +             42
+LL ~         }],
+   |
+
+error: this match could be written as a `let` statement
+  --> tests/ui/match_single_binding.rs:263:13
+   |
+LL | /             match 1 {
+LL | |
+LL | |                 _n => 42,
+LL | |             }
+   | |_____________^
+   |
+help: consider using a `let` statement
+   |
+LL ~             let _n = 1;
+LL +             42
+   |
+
+error: aborting due to 29 previous errors
 
diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/const_trait.fixed b/src/tools/clippy/tests/ui/missing_const_for_fn/const_trait.fixed
new file mode 100644
index 00000000000..7e0d4fccaae
--- /dev/null
+++ b/src/tools/clippy/tests/ui/missing_const_for_fn/const_trait.fixed
@@ -0,0 +1,36 @@
+#![feature(const_trait_impl)]
+#![warn(clippy::missing_const_for_fn)]
+
+// Reduced test case from https://github.com/rust-lang/rust-clippy/issues/14658
+
+#[const_trait]
+trait ConstTrait {
+    fn method(self);
+}
+
+impl ConstTrait for u32 {
+    fn method(self) {}
+}
+
+impl const ConstTrait for u64 {
+    fn method(self) {}
+}
+
+fn cannot_be_const() {
+    0u32.method();
+}
+
+//~v missing_const_for_fn
+const fn can_be_const() {
+    0u64.method();
+}
+
+// False negative, see FIXME comment in `clipy_utils::qualify_min_const`
+fn could_be_const_but_does_not_trigger<T>(t: T)
+where
+    T: const ConstTrait,
+{
+    t.method();
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/const_trait.rs b/src/tools/clippy/tests/ui/missing_const_for_fn/const_trait.rs
new file mode 100644
index 00000000000..439da4622d7
--- /dev/null
+++ b/src/tools/clippy/tests/ui/missing_const_for_fn/const_trait.rs
@@ -0,0 +1,36 @@
+#![feature(const_trait_impl)]
+#![warn(clippy::missing_const_for_fn)]
+
+// Reduced test case from https://github.com/rust-lang/rust-clippy/issues/14658
+
+#[const_trait]
+trait ConstTrait {
+    fn method(self);
+}
+
+impl ConstTrait for u32 {
+    fn method(self) {}
+}
+
+impl const ConstTrait for u64 {
+    fn method(self) {}
+}
+
+fn cannot_be_const() {
+    0u32.method();
+}
+
+//~v missing_const_for_fn
+fn can_be_const() {
+    0u64.method();
+}
+
+// False negative, see FIXME comment in `clipy_utils::qualify_min_const`
+fn could_be_const_but_does_not_trigger<T>(t: T)
+where
+    T: const ConstTrait,
+{
+    t.method();
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/const_trait.stderr b/src/tools/clippy/tests/ui/missing_const_for_fn/const_trait.stderr
new file mode 100644
index 00000000000..b994b88fac6
--- /dev/null
+++ b/src/tools/clippy/tests/ui/missing_const_for_fn/const_trait.stderr
@@ -0,0 +1,17 @@
+error: this could be a `const fn`
+  --> tests/ui/missing_const_for_fn/const_trait.rs:24:1
+   |
+LL | / fn can_be_const() {
+LL | |     0u64.method();
+LL | | }
+   | |_^
+   |
+   = note: `-D clippy::missing-const-for-fn` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::missing_const_for_fn)]`
+help: make the function `const`
+   |
+LL | const fn can_be_const() {
+   | +++++
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/clippy/tests/ui/needless_lifetimes.fixed b/src/tools/clippy/tests/ui/needless_lifetimes.fixed
index ceea4480d0d..15ca409c95b 100644
--- a/src/tools/clippy/tests/ui/needless_lifetimes.fixed
+++ b/src/tools/clippy/tests/ui/needless_lifetimes.fixed
@@ -10,7 +10,7 @@
     clippy::unnecessary_wraps,
     dyn_drop,
     clippy::get_first,
-    mismatched_lifetime_syntaxes,
+    mismatched_lifetime_syntaxes
 )]
 
 extern crate proc_macros;
diff --git a/src/tools/clippy/tests/ui/needless_lifetimes.rs b/src/tools/clippy/tests/ui/needless_lifetimes.rs
index 8432f9e6d2f..af9649d7298 100644
--- a/src/tools/clippy/tests/ui/needless_lifetimes.rs
+++ b/src/tools/clippy/tests/ui/needless_lifetimes.rs
@@ -10,7 +10,7 @@
     clippy::unnecessary_wraps,
     dyn_drop,
     clippy::get_first,
-    mismatched_lifetime_syntaxes,
+    mismatched_lifetime_syntaxes
 )]
 
 extern crate proc_macros;
diff --git a/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.fixed b/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.fixed
index 23dbee5a084..6915e1984fb 100644
--- a/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.fixed
+++ b/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.fixed
@@ -195,3 +195,19 @@ impl PartialOrd for K {
     //~^ non_canonical_partial_ord_impl
     fn partial_cmp(&self, other: &Self) -> Option<Ordering> { Some(self.cmp(other)) }
 }
+
+// #14574, check that partial_cmp invokes other.cmp
+
+#[derive(Eq, PartialEq)]
+struct L(u32);
+
+impl Ord for L {
+    fn cmp(&self, other: &Self) -> Ordering {
+        todo!();
+    }
+}
+
+impl PartialOrd for L {
+    //~^ non_canonical_partial_ord_impl
+    fn partial_cmp(&self, other: &Self) -> Option<Ordering> { Some(self.cmp(other)) }
+}
diff --git a/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.rs b/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.rs
index 12f055a542b..7ce4cdc9aec 100644
--- a/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.rs
+++ b/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.rs
@@ -201,3 +201,21 @@ impl PartialOrd for K {
         Ordering::Greater.into()
     }
 }
+
+// #14574, check that partial_cmp invokes other.cmp
+
+#[derive(Eq, PartialEq)]
+struct L(u32);
+
+impl Ord for L {
+    fn cmp(&self, other: &Self) -> Ordering {
+        todo!();
+    }
+}
+
+impl PartialOrd for L {
+    //~^ non_canonical_partial_ord_impl
+    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+        Some(other.cmp(self))
+    }
+}
diff --git a/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.stderr b/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.stderr
index c7de968588f..9bd6b1f726d 100644
--- a/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.stderr
+++ b/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.stderr
@@ -44,5 +44,18 @@ LL | ||     }
 LL | |  }
    | |__^
 
-error: aborting due to 3 previous errors
+error: non-canonical implementation of `partial_cmp` on an `Ord` type
+  --> tests/ui/non_canonical_partial_ord_impl.rs:216:1
+   |
+LL | /  impl PartialOrd for L {
+LL | |
+LL | |      fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+   | | _____________________________________________________________-
+LL | ||         Some(other.cmp(self))
+LL | ||     }
+   | ||_____- help: change this to: `{ Some(self.cmp(other)) }`
+LL | |  }
+   | |__^
+
+error: aborting due to 4 previous errors
 
diff --git a/src/tools/clippy/tests/ui/pointer_format.rs b/src/tools/clippy/tests/ui/pointer_format.rs
new file mode 100644
index 00000000000..0621f966ad1
--- /dev/null
+++ b/src/tools/clippy/tests/ui/pointer_format.rs
@@ -0,0 +1,66 @@
+#![warn(clippy::pointer_format)]
+
+use core::fmt::Debug;
+use core::marker::PhantomData;
+
+#[derive(Debug)]
+struct ContainsPointerDeep {
+    w: WithPointer,
+}
+
+struct ManualDebug {
+    ptr: *const u8,
+}
+
+#[derive(Debug)]
+struct WithPointer {
+    len: usize,
+    ptr: *const u8,
+}
+
+impl std::fmt::Debug for ManualDebug {
+    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+        f.write_str("ManualDebug")
+    }
+}
+
+trait Foo {
+    type Assoc: Foo + Debug;
+}
+
+#[derive(Debug)]
+struct S<T: Foo + 'static>(&'static S<T::Assoc>, PhantomData<T>);
+
+#[allow(unused)]
+fn unbounded<T: Foo + Debug + 'static>(s: &S<T>) {
+    format!("{s:?}");
+}
+
+fn main() {
+    let m = &(main as fn());
+    let g = &0;
+    let o = &format!("{m:p}");
+    //~^ pointer_format
+    let _ = format!("{m:?}");
+    //~^ pointer_format
+    println!("{g:p}");
+    //~^ pointer_format
+    panic!("{o:p}");
+    //~^ pointer_format
+    let answer = 42;
+    let x = &raw const answer;
+    let arr = [0u8; 8];
+    let with_ptr = WithPointer { len: 8, ptr: &arr as _ };
+    let _ = format!("{x:?}");
+    //~^ pointer_format
+    print!("{with_ptr:?}");
+    //~^ pointer_format
+    let container = ContainsPointerDeep { w: with_ptr };
+    print!("{container:?}");
+    //~^ pointer_format
+
+    let no_pointer = "foo";
+    println!("{no_pointer:?}");
+    let manual_debug = ManualDebug { ptr: &arr as _ };
+    println!("{manual_debug:?}");
+}
diff --git a/src/tools/clippy/tests/ui/pointer_format.stderr b/src/tools/clippy/tests/ui/pointer_format.stderr
new file mode 100644
index 00000000000..21ba39b8f8c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/pointer_format.stderr
@@ -0,0 +1,47 @@
+error: pointer formatting detected
+  --> tests/ui/pointer_format.rs:42:23
+   |
+LL |     let o = &format!("{m:p}");
+   |                       ^^^^^
+   |
+   = note: `-D clippy::pointer-format` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::pointer_format)]`
+
+error: pointer formatting detected
+  --> tests/ui/pointer_format.rs:44:22
+   |
+LL |     let _ = format!("{m:?}");
+   |                      ^^^^^
+
+error: pointer formatting detected
+  --> tests/ui/pointer_format.rs:46:15
+   |
+LL |     println!("{g:p}");
+   |               ^^^^^
+
+error: pointer formatting detected
+  --> tests/ui/pointer_format.rs:48:13
+   |
+LL |     panic!("{o:p}");
+   |             ^^^^^
+
+error: pointer formatting detected
+  --> tests/ui/pointer_format.rs:54:22
+   |
+LL |     let _ = format!("{x:?}");
+   |                      ^^^^^
+
+error: pointer formatting detected
+  --> tests/ui/pointer_format.rs:56:13
+   |
+LL |     print!("{with_ptr:?}");
+   |             ^^^^^^^^^^^^
+
+error: pointer formatting detected
+  --> tests/ui/pointer_format.rs:59:13
+   |
+LL |     print!("{container:?}");
+   |             ^^^^^^^^^^^^^
+
+error: aborting due to 7 previous errors
+
diff --git a/src/tools/clippy/tests/ui/print_literal.fixed b/src/tools/clippy/tests/ui/print_literal.fixed
index 24c45a4a61b..ebfe19c700e 100644
--- a/src/tools/clippy/tests/ui/print_literal.fixed
+++ b/src/tools/clippy/tests/ui/print_literal.fixed
@@ -94,3 +94,14 @@ fn issue_13959() {
 "
     );
 }
+
+fn issue_14930() {
+    println!("Hello x is {0:2$.1$}", 0.01, 2, 3);
+    //~^ print_literal
+    println!("Hello x is {0:2$.1$}", 0.01, 2, 3);
+    //~^ print_literal
+    println!("Hello x is {0:2$.1$}", 0.01, 2, 3);
+    //~^ print_literal
+    println!("Hello x is {0:2$.1$}", 0.01, 2, 3);
+    //~^ print_literal
+}
diff --git a/src/tools/clippy/tests/ui/print_literal.rs b/src/tools/clippy/tests/ui/print_literal.rs
index 42ae589ca63..8f3d9be0698 100644
--- a/src/tools/clippy/tests/ui/print_literal.rs
+++ b/src/tools/clippy/tests/ui/print_literal.rs
@@ -95,3 +95,14 @@ fn issue_13959() {
 "#
     );
 }
+
+fn issue_14930() {
+    println!("Hello {3} is {0:2$.1$}", 0.01, 2, 3, "x");
+    //~^ print_literal
+    println!("Hello {2} is {0:3$.1$}", 0.01, 2, "x", 3);
+    //~^ print_literal
+    println!("Hello {1} is {0:3$.2$}", 0.01, "x", 2, 3);
+    //~^ print_literal
+    println!("Hello {0} is {1:3$.2$}", "x", 0.01, 2, 3);
+    //~^ print_literal
+}
diff --git a/src/tools/clippy/tests/ui/print_literal.stderr b/src/tools/clippy/tests/ui/print_literal.stderr
index da663000686..1c378017d15 100644
--- a/src/tools/clippy/tests/ui/print_literal.stderr
+++ b/src/tools/clippy/tests/ui/print_literal.stderr
@@ -229,5 +229,53 @@ LL +         bar
 LL ~ "
    |
 
-error: aborting due to 18 previous errors
+error: literal with an empty format string
+  --> tests/ui/print_literal.rs:100:52
+   |
+LL |     println!("Hello {3} is {0:2$.1$}", 0.01, 2, 3, "x");
+   |                                                    ^^^
+   |
+help: try
+   |
+LL -     println!("Hello {3} is {0:2$.1$}", 0.01, 2, 3, "x");
+LL +     println!("Hello x is {0:2$.1$}", 0.01, 2, 3);
+   |
+
+error: literal with an empty format string
+  --> tests/ui/print_literal.rs:102:49
+   |
+LL |     println!("Hello {2} is {0:3$.1$}", 0.01, 2, "x", 3);
+   |                                                 ^^^
+   |
+help: try
+   |
+LL -     println!("Hello {2} is {0:3$.1$}", 0.01, 2, "x", 3);
+LL +     println!("Hello x is {0:2$.1$}", 0.01, 2, 3);
+   |
+
+error: literal with an empty format string
+  --> tests/ui/print_literal.rs:104:46
+   |
+LL |     println!("Hello {1} is {0:3$.2$}", 0.01, "x", 2, 3);
+   |                                              ^^^
+   |
+help: try
+   |
+LL -     println!("Hello {1} is {0:3$.2$}", 0.01, "x", 2, 3);
+LL +     println!("Hello x is {0:2$.1$}", 0.01, 2, 3);
+   |
+
+error: literal with an empty format string
+  --> tests/ui/print_literal.rs:106:40
+   |
+LL |     println!("Hello {0} is {1:3$.2$}", "x", 0.01, 2, 3);
+   |                                        ^^^
+   |
+help: try
+   |
+LL -     println!("Hello {0} is {1:3$.2$}", "x", 0.01, 2, 3);
+LL +     println!("Hello x is {0:2$.1$}", 0.01, 2, 3);
+   |
+
+error: aborting due to 22 previous errors
 
diff --git a/src/tools/clippy/tests/ui/semicolon_outside_block.fixed b/src/tools/clippy/tests/ui/semicolon_outside_block.fixed
index 52fae9a3aff..a3be80b4928 100644
--- a/src/tools/clippy/tests/ui/semicolon_outside_block.fixed
+++ b/src/tools/clippy/tests/ui/semicolon_outside_block.fixed
@@ -96,3 +96,28 @@ fn main() {
 
     unit_fn_block()
 }
+
+fn issue14926() {
+    macro_rules! gen_code {
+        [$l:lifetime: $b:block, $b2: block $(,)?] => {
+            $l: loop {
+                $b
+                break $l;
+            }
+            $l: loop {
+                $b2
+                break $l;
+            }
+        };
+    }
+
+    gen_code! {
+        'root:
+        {
+            println!("Block1");
+        },
+        {
+            println!("Block2");
+        },
+    }
+}
diff --git a/src/tools/clippy/tests/ui/semicolon_outside_block.rs b/src/tools/clippy/tests/ui/semicolon_outside_block.rs
index 5975e66fbb8..3b7bf68029f 100644
--- a/src/tools/clippy/tests/ui/semicolon_outside_block.rs
+++ b/src/tools/clippy/tests/ui/semicolon_outside_block.rs
@@ -96,3 +96,28 @@ fn main() {
 
     unit_fn_block()
 }
+
+fn issue14926() {
+    macro_rules! gen_code {
+        [$l:lifetime: $b:block, $b2: block $(,)?] => {
+            $l: loop {
+                $b
+                break $l;
+            }
+            $l: loop {
+                $b2
+                break $l;
+            }
+        };
+    }
+
+    gen_code! {
+        'root:
+        {
+            println!("Block1");
+        },
+        {
+            println!("Block2");
+        },
+    }
+}
diff --git a/src/tools/clippy/tests/ui/std_instead_of_core.fixed b/src/tools/clippy/tests/ui/std_instead_of_core.fixed
index ab2e801eee2..1820ade422f 100644
--- a/src/tools/clippy/tests/ui/std_instead_of_core.fixed
+++ b/src/tools/clippy/tests/ui/std_instead_of_core.fixed
@@ -90,3 +90,9 @@ fn msrv_1_76(_: std::net::IpAddr) {}
 #[clippy::msrv = "1.77"]
 fn msrv_1_77(_: core::net::IpAddr) {}
 //~^ std_instead_of_core
+
+#[warn(clippy::std_instead_of_core)]
+#[rustfmt::skip]
+fn issue14982() {
+    use std::{collections::HashMap, hash::Hash};
+}
diff --git a/src/tools/clippy/tests/ui/std_instead_of_core.rs b/src/tools/clippy/tests/ui/std_instead_of_core.rs
index f760b3561ae..32c49330981 100644
--- a/src/tools/clippy/tests/ui/std_instead_of_core.rs
+++ b/src/tools/clippy/tests/ui/std_instead_of_core.rs
@@ -90,3 +90,9 @@ fn msrv_1_76(_: std::net::IpAddr) {}
 #[clippy::msrv = "1.77"]
 fn msrv_1_77(_: std::net::IpAddr) {}
 //~^ std_instead_of_core
+
+#[warn(clippy::std_instead_of_core)]
+#[rustfmt::skip]
+fn issue14982() {
+    use std::{collections::HashMap, hash::Hash};
+}
diff --git a/src/tools/clippy/tests/ui/unit_arg.rs b/src/tools/clippy/tests/ui/unit_arg.rs
index 22a6a26dab6..4208efad677 100644
--- a/src/tools/clippy/tests/ui/unit_arg.rs
+++ b/src/tools/clippy/tests/ui/unit_arg.rs
@@ -151,3 +151,27 @@ fn main() {
     bad();
     ok();
 }
+
+fn issue14857() {
+    let fn_take_unit = |_: ()| {};
+    fn some_other_fn(_: &i32) {}
+
+    macro_rules! mac {
+        (def) => {
+            Default::default()
+        };
+        (func $f:expr) => {
+            $f()
+        };
+        (nonempty_block $e:expr) => {{
+            some_other_fn(&$e);
+            $e
+        }};
+    }
+    fn_take_unit(mac!(def));
+    //~^ unit_arg
+    fn_take_unit(mac!(func Default::default));
+    //~^ unit_arg
+    fn_take_unit(mac!(nonempty_block Default::default()));
+    //~^ unit_arg
+}
diff --git a/src/tools/clippy/tests/ui/unit_arg.stderr b/src/tools/clippy/tests/ui/unit_arg.stderr
index 6c333d9792d..0dcfb02b9fa 100644
--- a/src/tools/clippy/tests/ui/unit_arg.stderr
+++ b/src/tools/clippy/tests/ui/unit_arg.stderr
@@ -199,5 +199,26 @@ LL ~     foo(1);
 LL +     Some(())
    |
 
-error: aborting due to 10 previous errors
+error: passing a unit value to a function
+  --> tests/ui/unit_arg.rs:171:5
+   |
+LL |     fn_take_unit(mac!(def));
+   |     ^^^^^^^^^^^^^^^^^^^^^^^
+   |
+
+error: passing a unit value to a function
+  --> tests/ui/unit_arg.rs:173:5
+   |
+LL |     fn_take_unit(mac!(func Default::default));
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+
+error: passing a unit value to a function
+  --> tests/ui/unit_arg.rs:175:5
+   |
+LL |     fn_take_unit(mac!(nonempty_block Default::default()));
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+
+error: aborting due to 13 previous errors
 
diff --git a/src/tools/clippy/tests/ui/unit_arg_empty_blocks.fixed b/src/tools/clippy/tests/ui/unit_arg_empty_blocks.fixed
deleted file mode 100644
index b045a33608d..00000000000
--- a/src/tools/clippy/tests/ui/unit_arg_empty_blocks.fixed
+++ /dev/null
@@ -1,34 +0,0 @@
-#![warn(clippy::unit_arg)]
-#![allow(unused_must_use, unused_variables)]
-#![allow(clippy::no_effect, clippy::uninlined_format_args)]
-
-use std::fmt::Debug;
-
-fn foo<T: Debug>(t: T) {
-    println!("{:?}", t);
-}
-
-fn foo3<T1: Debug, T2: Debug, T3: Debug>(t1: T1, t2: T2, t3: T3) {
-    println!("{:?}, {:?}, {:?}", t1, t2, t3);
-}
-
-fn bad() {
-    foo(());
-    //~^ unit_arg
-    foo3((), 2, 2);
-    //~^ unit_arg
-    foo(0);
-    taking_two_units((), ());
-    //~^ unit_arg
-    foo(0);
-    foo(1);
-    taking_three_units((), (), ());
-    //~^ unit_arg
-}
-
-fn taking_two_units(a: (), b: ()) {}
-fn taking_three_units(a: (), b: (), c: ()) {}
-
-fn main() {
-    bad();
-}
diff --git a/src/tools/clippy/tests/ui/unit_arg_empty_blocks.rs b/src/tools/clippy/tests/ui/unit_arg_empty_blocks.rs
deleted file mode 100644
index ab305913f3f..00000000000
--- a/src/tools/clippy/tests/ui/unit_arg_empty_blocks.rs
+++ /dev/null
@@ -1,31 +0,0 @@
-#![warn(clippy::unit_arg)]
-#![allow(unused_must_use, unused_variables)]
-#![allow(clippy::no_effect, clippy::uninlined_format_args)]
-
-use std::fmt::Debug;
-
-fn foo<T: Debug>(t: T) {
-    println!("{:?}", t);
-}
-
-fn foo3<T1: Debug, T2: Debug, T3: Debug>(t1: T1, t2: T2, t3: T3) {
-    println!("{:?}, {:?}, {:?}", t1, t2, t3);
-}
-
-fn bad() {
-    foo({});
-    //~^ unit_arg
-    foo3({}, 2, 2);
-    //~^ unit_arg
-    taking_two_units({}, foo(0));
-    //~^ unit_arg
-    taking_three_units({}, foo(0), foo(1));
-    //~^ unit_arg
-}
-
-fn taking_two_units(a: (), b: ()) {}
-fn taking_three_units(a: (), b: (), c: ()) {}
-
-fn main() {
-    bad();
-}
diff --git a/src/tools/clippy/tests/ui/unit_arg_empty_blocks.stderr b/src/tools/clippy/tests/ui/unit_arg_empty_blocks.stderr
deleted file mode 100644
index 2c686d58ecc..00000000000
--- a/src/tools/clippy/tests/ui/unit_arg_empty_blocks.stderr
+++ /dev/null
@@ -1,46 +0,0 @@
-error: passing a unit value to a function
-  --> tests/ui/unit_arg_empty_blocks.rs:16:5
-   |
-LL |     foo({});
-   |     ^^^^--^
-   |         |
-   |         help: use a unit literal instead: `()`
-   |
-   = note: `-D clippy::unit-arg` implied by `-D warnings`
-   = help: to override `-D warnings` add `#[allow(clippy::unit_arg)]`
-
-error: passing a unit value to a function
-  --> tests/ui/unit_arg_empty_blocks.rs:18:5
-   |
-LL |     foo3({}, 2, 2);
-   |     ^^^^^--^^^^^^^
-   |          |
-   |          help: use a unit literal instead: `()`
-
-error: passing unit values to a function
-  --> tests/ui/unit_arg_empty_blocks.rs:20:5
-   |
-LL |     taking_two_units({}, foo(0));
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-help: move the expression in front of the call and replace it with the unit literal `()`
-   |
-LL ~     foo(0);
-LL ~     taking_two_units((), ());
-   |
-
-error: passing unit values to a function
-  --> tests/ui/unit_arg_empty_blocks.rs:22:5
-   |
-LL |     taking_three_units({}, foo(0), foo(1));
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-help: move the expressions in front of the call and replace them with the unit literal `()`
-   |
-LL ~     foo(0);
-LL +     foo(1);
-LL ~     taking_three_units((), (), ());
-   |
-
-error: aborting due to 4 previous errors
-
diff --git a/src/tools/clippy/tests/ui/unit_arg_fixable.fixed b/src/tools/clippy/tests/ui/unit_arg_fixable.fixed
new file mode 100644
index 00000000000..03353a14081
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unit_arg_fixable.fixed
@@ -0,0 +1,78 @@
+#![warn(clippy::unit_arg)]
+#![allow(unused_must_use, unused_variables)]
+#![allow(clippy::no_effect, clippy::uninlined_format_args)]
+
+use std::fmt::Debug;
+
+fn foo<T: Debug>(t: T) {
+    println!("{:?}", t);
+}
+
+fn foo3<T1: Debug, T2: Debug, T3: Debug>(t1: T1, t2: T2, t3: T3) {
+    println!("{:?}, {:?}, {:?}", t1, t2, t3);
+}
+
+fn bad() {
+    foo(());
+    //~^ unit_arg
+    foo3((), 2, 2);
+    //~^ unit_arg
+    foo(0);
+    taking_two_units((), ());
+    //~^ unit_arg
+    foo(0);
+    foo(1);
+    taking_three_units((), (), ());
+    //~^ unit_arg
+}
+
+fn taking_two_units(a: (), b: ()) {}
+fn taking_three_units(a: (), b: (), c: ()) {}
+
+fn main() {
+    bad();
+}
+
+fn issue14857() {
+    let fn_take_unit = |_: ()| {};
+    fn_take_unit(());
+    //~^ unit_arg
+
+    fn some_other_fn(_: &i32) {}
+
+    macro_rules! another_mac {
+        () => {
+            some_other_fn(&Default::default())
+        };
+        ($e:expr) => {
+            some_other_fn(&$e)
+        };
+    }
+
+    another_mac!();
+    fn_take_unit(());
+    //~^ unit_arg
+    another_mac!(1);
+    fn_take_unit(());
+    //~^ unit_arg
+
+    macro_rules! mac {
+        (nondef $e:expr) => {
+            $e
+        };
+        (empty_block) => {{}};
+    }
+    fn_take_unit(mac!(nondef ()));
+    //~^ unit_arg
+    mac!(empty_block);
+    fn_take_unit(());
+    //~^ unit_arg
+
+    fn def<T: Default>() -> T {
+        Default::default()
+    }
+
+    let _: () = def();
+    fn_take_unit(());
+    //~^ unit_arg
+}
diff --git a/src/tools/clippy/tests/ui/unit_arg_fixable.rs b/src/tools/clippy/tests/ui/unit_arg_fixable.rs
new file mode 100644
index 00000000000..03fd96efdf9
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unit_arg_fixable.rs
@@ -0,0 +1,71 @@
+#![warn(clippy::unit_arg)]
+#![allow(unused_must_use, unused_variables)]
+#![allow(clippy::no_effect, clippy::uninlined_format_args)]
+
+use std::fmt::Debug;
+
+fn foo<T: Debug>(t: T) {
+    println!("{:?}", t);
+}
+
+fn foo3<T1: Debug, T2: Debug, T3: Debug>(t1: T1, t2: T2, t3: T3) {
+    println!("{:?}, {:?}, {:?}", t1, t2, t3);
+}
+
+fn bad() {
+    foo({});
+    //~^ unit_arg
+    foo3({}, 2, 2);
+    //~^ unit_arg
+    taking_two_units({}, foo(0));
+    //~^ unit_arg
+    taking_three_units({}, foo(0), foo(1));
+    //~^ unit_arg
+}
+
+fn taking_two_units(a: (), b: ()) {}
+fn taking_three_units(a: (), b: (), c: ()) {}
+
+fn main() {
+    bad();
+}
+
+fn issue14857() {
+    let fn_take_unit = |_: ()| {};
+    fn_take_unit(Default::default());
+    //~^ unit_arg
+
+    fn some_other_fn(_: &i32) {}
+
+    macro_rules! another_mac {
+        () => {
+            some_other_fn(&Default::default())
+        };
+        ($e:expr) => {
+            some_other_fn(&$e)
+        };
+    }
+
+    fn_take_unit(another_mac!());
+    //~^ unit_arg
+    fn_take_unit(another_mac!(1));
+    //~^ unit_arg
+
+    macro_rules! mac {
+        (nondef $e:expr) => {
+            $e
+        };
+        (empty_block) => {{}};
+    }
+    fn_take_unit(mac!(nondef Default::default()));
+    //~^ unit_arg
+    fn_take_unit(mac!(empty_block));
+    //~^ unit_arg
+
+    fn def<T: Default>() -> T {
+        Default::default()
+    }
+
+    fn_take_unit(def());
+    //~^ unit_arg
+}
diff --git a/src/tools/clippy/tests/ui/unit_arg_fixable.stderr b/src/tools/clippy/tests/ui/unit_arg_fixable.stderr
new file mode 100644
index 00000000000..ccd5aa8322f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unit_arg_fixable.stderr
@@ -0,0 +1,110 @@
+error: passing a unit value to a function
+  --> tests/ui/unit_arg_fixable.rs:16:5
+   |
+LL |     foo({});
+   |     ^^^^--^
+   |         |
+   |         help: use a unit literal instead: `()`
+   |
+   = note: `-D clippy::unit-arg` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::unit_arg)]`
+
+error: passing a unit value to a function
+  --> tests/ui/unit_arg_fixable.rs:18:5
+   |
+LL |     foo3({}, 2, 2);
+   |     ^^^^^--^^^^^^^
+   |          |
+   |          help: use a unit literal instead: `()`
+
+error: passing unit values to a function
+  --> tests/ui/unit_arg_fixable.rs:20:5
+   |
+LL |     taking_two_units({}, foo(0));
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: move the expression in front of the call and replace it with the unit literal `()`
+   |
+LL ~     foo(0);
+LL ~     taking_two_units((), ());
+   |
+
+error: passing unit values to a function
+  --> tests/ui/unit_arg_fixable.rs:22:5
+   |
+LL |     taking_three_units({}, foo(0), foo(1));
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: move the expressions in front of the call and replace them with the unit literal `()`
+   |
+LL ~     foo(0);
+LL +     foo(1);
+LL ~     taking_three_units((), (), ());
+   |
+
+error: passing a unit value to a function
+  --> tests/ui/unit_arg_fixable.rs:35:5
+   |
+LL |     fn_take_unit(Default::default());
+   |     ^^^^^^^^^^^^^------------------^
+   |                  |
+   |                  help: use a unit literal instead: `()`
+
+error: passing a unit value to a function
+  --> tests/ui/unit_arg_fixable.rs:49:5
+   |
+LL |     fn_take_unit(another_mac!());
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: move the expression in front of the call and replace it with the unit literal `()`
+   |
+LL ~     another_mac!();
+LL ~     fn_take_unit(());
+   |
+
+error: passing a unit value to a function
+  --> tests/ui/unit_arg_fixable.rs:51:5
+   |
+LL |     fn_take_unit(another_mac!(1));
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: move the expression in front of the call and replace it with the unit literal `()`
+   |
+LL ~     another_mac!(1);
+LL ~     fn_take_unit(());
+   |
+
+error: passing a unit value to a function
+  --> tests/ui/unit_arg_fixable.rs:60:5
+   |
+LL |     fn_take_unit(mac!(nondef Default::default()));
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^------------------^^
+   |                              |
+   |                              help: use a unit literal instead: `()`
+
+error: passing a unit value to a function
+  --> tests/ui/unit_arg_fixable.rs:62:5
+   |
+LL |     fn_take_unit(mac!(empty_block));
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: move the expression in front of the call and replace it with the unit literal `()`
+   |
+LL ~     mac!(empty_block);
+LL ~     fn_take_unit(());
+   |
+
+error: passing a unit value to a function
+  --> tests/ui/unit_arg_fixable.rs:69:5
+   |
+LL |     fn_take_unit(def());
+   |     ^^^^^^^^^^^^^^^^^^^
+   |
+help: move the expression in front of the call and replace it with the unit literal `()`
+   |
+LL ~     let _: () = def();
+LL ~     fn_take_unit(());
+   |
+
+error: aborting due to 10 previous errors
+
diff --git a/src/tools/clippy/tests/ui/unused_unit.edition2021.fixed b/src/tools/clippy/tests/ui/unused_unit.edition2021.fixed
index 93dd58b8e9d..def8ef86e3c 100644
--- a/src/tools/clippy/tests/ui/unused_unit.edition2021.fixed
+++ b/src/tools/clippy/tests/ui/unused_unit.edition2021.fixed
@@ -143,4 +143,10 @@ mod issue14577 {
             todo!()
         }
     }
-}
\ No newline at end of file
+}
+
+mod pr14962 {
+    #[allow(unused_parens)]
+    type UnusedParensButNoUnit = Box<dyn (Fn())>;
+}
+
diff --git a/src/tools/clippy/tests/ui/unused_unit.edition2024.fixed b/src/tools/clippy/tests/ui/unused_unit.edition2024.fixed
index 987d901b97d..f908b958b19 100644
--- a/src/tools/clippy/tests/ui/unused_unit.edition2024.fixed
+++ b/src/tools/clippy/tests/ui/unused_unit.edition2024.fixed
@@ -143,4 +143,10 @@ mod issue14577 {
             todo!()
         }
     }
-}
\ No newline at end of file
+}
+
+mod pr14962 {
+    #[allow(unused_parens)]
+    type UnusedParensButNoUnit = Box<dyn (Fn())>;
+}
+
diff --git a/src/tools/clippy/tests/ui/unused_unit.rs b/src/tools/clippy/tests/ui/unused_unit.rs
index b7645f7b6a2..7298ec40cc2 100644
--- a/src/tools/clippy/tests/ui/unused_unit.rs
+++ b/src/tools/clippy/tests/ui/unused_unit.rs
@@ -143,4 +143,10 @@ mod issue14577 {
             todo!()
         }
     }
-}
\ No newline at end of file
+}
+
+mod pr14962 {
+    #[allow(unused_parens)]
+    type UnusedParensButNoUnit = Box<dyn (Fn())>;
+}
+
diff --git a/src/tools/clippy/tests/ui/write_literal.fixed b/src/tools/clippy/tests/ui/write_literal.fixed
index e84f768e33d..29352fd468e 100644
--- a/src/tools/clippy/tests/ui/write_literal.fixed
+++ b/src/tools/clippy/tests/ui/write_literal.fixed
@@ -87,3 +87,15 @@ fn issue_13959() {
 "
     );
 }
+
+fn issue_14930() {
+    let mut v = Vec::new();
+    writeln!(v, "Hello x is {0:2$.1$}", 0.01, 2, 3);
+    //~^ write_literal
+    writeln!(v, "Hello x is {0:2$.1$}", 0.01, 2, 3);
+    //~^ write_literal
+    writeln!(v, "Hello x is {0:2$.1$}", 0.01, 2, 3);
+    //~^ write_literal
+    writeln!(v, "Hello x is {0:2$.1$}", 0.01, 2, 3);
+    //~^ write_literal
+}
diff --git a/src/tools/clippy/tests/ui/write_literal.rs b/src/tools/clippy/tests/ui/write_literal.rs
index fc29fcbede7..92872752759 100644
--- a/src/tools/clippy/tests/ui/write_literal.rs
+++ b/src/tools/clippy/tests/ui/write_literal.rs
@@ -88,3 +88,15 @@ fn issue_13959() {
 "#
     );
 }
+
+fn issue_14930() {
+    let mut v = Vec::new();
+    writeln!(v, "Hello {3} is {0:2$.1$}", 0.01, 2, 3, "x");
+    //~^ write_literal
+    writeln!(v, "Hello {2} is {0:3$.1$}", 0.01, 2, "x", 3);
+    //~^ write_literal
+    writeln!(v, "Hello {1} is {0:3$.2$}", 0.01, "x", 2, 3);
+    //~^ write_literal
+    writeln!(v, "Hello {0} is {1:3$.2$}", "x", 0.01, 2, 3);
+    //~^ write_literal
+}
diff --git a/src/tools/clippy/tests/ui/write_literal.stderr b/src/tools/clippy/tests/ui/write_literal.stderr
index d53c2a7de2e..ca37406c811 100644
--- a/src/tools/clippy/tests/ui/write_literal.stderr
+++ b/src/tools/clippy/tests/ui/write_literal.stderr
@@ -181,5 +181,53 @@ LL +         bar
 LL ~ "
    |
 
-error: aborting due to 14 previous errors
+error: literal with an empty format string
+  --> tests/ui/write_literal.rs:94:55
+   |
+LL |     writeln!(v, "Hello {3} is {0:2$.1$}", 0.01, 2, 3, "x");
+   |                                                       ^^^
+   |
+help: try
+   |
+LL -     writeln!(v, "Hello {3} is {0:2$.1$}", 0.01, 2, 3, "x");
+LL +     writeln!(v, "Hello x is {0:2$.1$}", 0.01, 2, 3);
+   |
+
+error: literal with an empty format string
+  --> tests/ui/write_literal.rs:96:52
+   |
+LL |     writeln!(v, "Hello {2} is {0:3$.1$}", 0.01, 2, "x", 3);
+   |                                                    ^^^
+   |
+help: try
+   |
+LL -     writeln!(v, "Hello {2} is {0:3$.1$}", 0.01, 2, "x", 3);
+LL +     writeln!(v, "Hello x is {0:2$.1$}", 0.01, 2, 3);
+   |
+
+error: literal with an empty format string
+  --> tests/ui/write_literal.rs:98:49
+   |
+LL |     writeln!(v, "Hello {1} is {0:3$.2$}", 0.01, "x", 2, 3);
+   |                                                 ^^^
+   |
+help: try
+   |
+LL -     writeln!(v, "Hello {1} is {0:3$.2$}", 0.01, "x", 2, 3);
+LL +     writeln!(v, "Hello x is {0:2$.1$}", 0.01, 2, 3);
+   |
+
+error: literal with an empty format string
+  --> tests/ui/write_literal.rs:100:43
+   |
+LL |     writeln!(v, "Hello {0} is {1:3$.2$}", "x", 0.01, 2, 3);
+   |                                           ^^^
+   |
+help: try
+   |
+LL -     writeln!(v, "Hello {0} is {1:3$.2$}", "x", 0.01, 2, 3);
+LL +     writeln!(v, "Hello x is {0:2$.1$}", 0.01, 2, 3);
+   |
+
+error: aborting due to 18 previous errors
 
diff --git a/src/tools/clippy/tests/ui/zombie_processes.rs b/src/tools/clippy/tests/ui/zombie_processes.rs
index 395f9dd2def..e81b5fd4f3e 100644
--- a/src/tools/clippy/tests/ui/zombie_processes.rs
+++ b/src/tools/clippy/tests/ui/zombie_processes.rs
@@ -198,3 +198,13 @@ mod issue14677 {
         child.wait().unwrap();
     }
 }
+
+fn issue14911() -> std::io::Result<String> {
+    let (mut recv, send) = std::io::pipe()?;
+    let mut command = Command::new("ls")
+        .stdout(send.try_clone()?)
+        .spawn()
+        .expect("Could not spawn new process...");
+    command.wait()?;
+    Ok("".into())
+}
diff --git a/src/tools/clippy/util/gh-pages/script.js b/src/tools/clippy/util/gh-pages/script.js
index fec883938d6..285aa34e701 100644
--- a/src/tools/clippy/util/gh-pages/script.js
+++ b/src/tools/clippy/util/gh-pages/script.js
@@ -602,7 +602,7 @@ filters.filterLints();
 updateLintCount();
 
 function updateLintCount() {
-    const allLints = filters.getAllLints();
+    const allLints = filters.getAllLints().filter(lint => lint.group != "deprecated");
     const totalLints = allLints.length;
     
     const countElement = document.getElementById("lint-count");
diff --git a/src/tools/compiletest/src/directive-list.rs b/src/tools/compiletest/src/directive-list.rs
index 1406553c9ea..2ecb4fc8652 100644
--- a/src/tools/compiletest/src/directive-list.rs
+++ b/src/tools/compiletest/src/directive-list.rs
@@ -166,6 +166,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
     "needs-subprocess",
     "needs-symlink",
     "needs-target-has-atomic",
+    "needs-target-std",
     "needs-threads",
     "needs-unwind",
     "needs-wasmtime",
diff --git a/src/tools/compiletest/src/header/needs.rs b/src/tools/compiletest/src/header/needs.rs
index 2ace40c490b..b1165f4bb18 100644
--- a/src/tools/compiletest/src/header/needs.rs
+++ b/src/tools/compiletest/src/header/needs.rs
@@ -174,6 +174,11 @@ pub(super) fn handle_needs(
             condition: config.with_std_debug_assertions,
             ignore_reason: "ignored if std wasn't built with debug assertions",
         },
+        Need {
+            name: "needs-target-std",
+            condition: build_helper::targets::target_supports_std(&config.target),
+            ignore_reason: "ignored if target does not support std",
+        },
     ];
 
     let (name, rest) = match ln.split_once([':', ' ']) {
diff --git a/src/tools/compiletest/src/header/tests.rs b/src/tools/compiletest/src/header/tests.rs
index e7e5ff0ab00..31b49b09bcd 100644
--- a/src/tools/compiletest/src/header/tests.rs
+++ b/src/tools/compiletest/src/header/tests.rs
@@ -945,3 +945,14 @@ fn test_ignore_auxiliary() {
     let config = cfg().build();
     assert!(check_ignore(&config, "//@ ignore-auxiliary"));
 }
+
+#[test]
+fn test_needs_target_std() {
+    // Cherry-picks two targets:
+    // 1. `x86_64-unknown-none`: Tier 2, intentionally never supports std.
+    // 2. `x86_64-unknown-linux-gnu`: Tier 1, always supports std.
+    let config = cfg().target("x86_64-unknown-none").build();
+    assert!(check_ignore(&config, "//@ needs-target-std"));
+    let config = cfg().target("x86_64-unknown-linux-gnu").build();
+    assert!(!check_ignore(&config, "//@ needs-target-std"));
+}
diff --git a/src/tools/miri/README.md b/src/tools/miri/README.md
index fc7942a49c0..5554c7975ff 100644
--- a/src/tools/miri/README.md
+++ b/src/tools/miri/README.md
@@ -286,6 +286,11 @@ environment variable. We first document the most relevant and most commonly used
   specific circumstances, but Miri's behavior will also be more stable across versions and targets.
   This is equivalent to `-Zmiri-fixed-schedule -Zmiri-compare-exchange-weak-failure-rate=0.0
   -Zmiri-address-reuse-cross-thread-rate=0.0 -Zmiri-disable-weak-memory-emulation`.
+* `-Zmiri-deterministic-floats` makes Miri's floating-point behavior fully deterministic. This means
+  that operations will always return the preferred NaN, imprecise operations will not have any
+  random error applied to them, and `min`/`max` as "maybe fused" multiply-add all behave
+  deterministically. Note that Miri still uses host floats for some operations, so behavior can
+  still differ depending on the host target and setup.
 * `-Zmiri-disable-isolation` disables host isolation. As a consequence,
   the program has access to host resources such as environment variables, file
   systems, and randomness.
diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version
index 43632bf86a7..73183983599 100644
--- a/src/tools/miri/rust-version
+++ b/src/tools/miri/rust-version
@@ -1 +1 @@
-c6768de2d63de7a41124a0fb8fc78f9e26111c01
+d087f112b7d1323446c7b39a8b616aee7fa56b3d
diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs
index 2faaec5a174..d4ba7fbd6a4 100644
--- a/src/tools/miri/src/bin/miri.rs
+++ b/src/tools/miri/src/bin/miri.rs
@@ -601,6 +601,8 @@ fn main() {
             miri_config.collect_leak_backtraces = false;
         } else if arg == "-Zmiri-force-intrinsic-fallback" {
             miri_config.force_intrinsic_fallback = true;
+        } else if arg == "-Zmiri-deterministic-floats" {
+            miri_config.float_nondet = false;
         } else if arg == "-Zmiri-strict-provenance" {
             miri_config.provenance_mode = ProvenanceMode::Strict;
         } else if arg == "-Zmiri-permissive-provenance" {
diff --git a/src/tools/miri/src/eval.rs b/src/tools/miri/src/eval.rs
index 6f5f756e144..7a5f96ec177 100644
--- a/src/tools/miri/src/eval.rs
+++ b/src/tools/miri/src/eval.rs
@@ -166,6 +166,8 @@ pub struct MiriConfig {
     pub fixed_scheduling: bool,
     /// Always prefer the intrinsic fallback body over the native Miri implementation.
     pub force_intrinsic_fallback: bool,
+    /// Whether floating-point operations can behave non-deterministically.
+    pub float_nondet: bool,
 }
 
 impl Default for MiriConfig {
@@ -205,6 +207,7 @@ impl Default for MiriConfig {
             address_reuse_cross_thread_rate: 0.1,
             fixed_scheduling: false,
             force_intrinsic_fallback: false,
+            float_nondet: true,
         }
     }
 }
diff --git a/src/tools/miri/src/intrinsics/mod.rs b/src/tools/miri/src/intrinsics/mod.rs
index 9957e351ff1..ec77a162cdb 100644
--- a/src/tools/miri/src/intrinsics/mod.rs
+++ b/src/tools/miri/src/intrinsics/mod.rs
@@ -272,8 +272,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 let a = this.read_scalar(a)?.to_f32()?;
                 let b = this.read_scalar(b)?.to_f32()?;
                 let c = this.read_scalar(c)?.to_f32()?;
-                // FIXME: Using host floats, to work around https://github.com/rust-lang/rustc_apfloat/issues/11
-                let res = a.to_host().mul_add(b.to_host(), c.to_host()).to_soft();
+                let res = a.mul_add(b, c).value;
                 let res = this.adjust_nan(res, &[a, b, c]);
                 this.write_scalar(res, dest)?;
             }
@@ -282,8 +281,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 let a = this.read_scalar(a)?.to_f64()?;
                 let b = this.read_scalar(b)?.to_f64()?;
                 let c = this.read_scalar(c)?.to_f64()?;
-                // FIXME: Using host floats, to work around https://github.com/rust-lang/rustc_apfloat/issues/11
-                let res = a.to_host().mul_add(b.to_host(), c.to_host()).to_soft();
+                let res = a.mul_add(b, c).value;
                 let res = this.adjust_nan(res, &[a, b, c]);
                 this.write_scalar(res, dest)?;
             }
@@ -293,13 +291,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 let a = this.read_scalar(a)?.to_f32()?;
                 let b = this.read_scalar(b)?.to_f32()?;
                 let c = this.read_scalar(c)?.to_f32()?;
-                let fuse: bool = this.machine.rng.get_mut().random();
-                let res = if fuse {
-                    // FIXME: Using host floats, to work around https://github.com/rust-lang/rustc_apfloat/issues/11
-                    a.to_host().mul_add(b.to_host(), c.to_host()).to_soft()
-                } else {
-                    ((a * b).value + c).value
-                };
+                let fuse: bool = this.machine.float_nondet && this.machine.rng.get_mut().random();
+                let res = if fuse { a.mul_add(b, c).value } else { ((a * b).value + c).value };
                 let res = this.adjust_nan(res, &[a, b, c]);
                 this.write_scalar(res, dest)?;
             }
@@ -308,13 +301,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 let a = this.read_scalar(a)?.to_f64()?;
                 let b = this.read_scalar(b)?.to_f64()?;
                 let c = this.read_scalar(c)?.to_f64()?;
-                let fuse: bool = this.machine.rng.get_mut().random();
-                let res = if fuse {
-                    // FIXME: Using host floats, to work around https://github.com/rust-lang/rustc_apfloat/issues/11
-                    a.to_host().mul_add(b.to_host(), c.to_host()).to_soft()
-                } else {
-                    ((a * b).value + c).value
-                };
+                let fuse: bool = this.machine.float_nondet && this.machine.rng.get_mut().random();
+                let res = if fuse { a.mul_add(b, c).value } else { ((a * b).value + c).value };
                 let res = this.adjust_nan(res, &[a, b, c]);
                 this.write_scalar(res, dest)?;
             }
diff --git a/src/tools/miri/src/intrinsics/simd.rs b/src/tools/miri/src/intrinsics/simd.rs
index b17fd4fb7f9..dbe193bdbda 100644
--- a/src/tools/miri/src/intrinsics/simd.rs
+++ b/src/tools/miri/src/intrinsics/simd.rs
@@ -306,7 +306,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                     let c = this.read_scalar(&this.project_index(&c, i)?)?;
                     let dest = this.project_index(&dest, i)?;
 
-                    let fuse: bool = intrinsic_name == "fma" || this.machine.rng.get_mut().random();
+                    let fuse: bool = intrinsic_name == "fma"
+                        || (this.machine.float_nondet && this.machine.rng.get_mut().random());
 
                     // Works for f32 and f64.
                     // FIXME: using host floats to work around https://github.com/rust-lang/miri/issues/2468.
@@ -320,7 +321,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                             let b = b.to_f32()?;
                             let c = c.to_f32()?;
                             let res = if fuse {
-                                a.to_host().mul_add(b.to_host(), c.to_host()).to_soft()
+                                a.mul_add(b, c).value
                             } else {
                                 ((a * b).value + c).value
                             };
@@ -332,7 +333,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                             let b = b.to_f64()?;
                             let c = c.to_f64()?;
                             let res = if fuse {
-                                a.to_host().mul_add(b.to_host(), c.to_host()).to_soft()
+                                a.mul_add(b, c).value
                             } else {
                                 ((a * b).value + c).value
                             };
diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs
index b221dd85092..b4d7db34efa 100644
--- a/src/tools/miri/src/machine.rs
+++ b/src/tools/miri/src/machine.rs
@@ -618,6 +618,9 @@ pub struct MiriMachine<'tcx> {
 
     /// Always prefer the intrinsic fallback body over the native Miri implementation.
     pub force_intrinsic_fallback: bool,
+
+    /// Whether floating-point operations can behave non-deterministically.
+    pub float_nondet: bool,
 }
 
 impl<'tcx> MiriMachine<'tcx> {
@@ -778,6 +781,7 @@ impl<'tcx> MiriMachine<'tcx> {
             int2ptr_warned: Default::default(),
             mangle_internal_symbol_cache: Default::default(),
             force_intrinsic_fallback: config.force_intrinsic_fallback,
+            float_nondet: config.float_nondet,
         }
     }
 
@@ -956,6 +960,7 @@ impl VisitProvenance for MiriMachine<'_> {
             int2ptr_warned: _,
             mangle_internal_symbol_cache: _,
             force_intrinsic_fallback: _,
+            float_nondet: _,
         } = self;
 
         threads.visit_provenance(visit);
diff --git a/src/tools/miri/src/math.rs b/src/tools/miri/src/math.rs
index d1355a21684..cf16a5676d6 100644
--- a/src/tools/miri/src/math.rs
+++ b/src/tools/miri/src/math.rs
@@ -15,6 +15,10 @@ pub(crate) fn apply_random_float_error<F: rustc_apfloat::Float>(
     val: F,
     err_scale: i32,
 ) -> F {
+    if !ecx.machine.float_nondet {
+        return val;
+    }
+
     let rng = ecx.machine.rng.get_mut();
     // Generate a random integer in the range [0, 2^PREC).
     // (When read as binary, the position of the first `1` determines the exponent,
diff --git a/src/tools/miri/src/operator.rs b/src/tools/miri/src/operator.rs
index 81f22b2d0b2..73d671121f6 100644
--- a/src/tools/miri/src/operator.rs
+++ b/src/tools/miri/src/operator.rs
@@ -76,6 +76,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     }
 
     fn generate_nan<F1: Float + FloatConvert<F2>, F2: Float>(&self, inputs: &[F1]) -> F2 {
+        let this = self.eval_context_ref();
+        if !this.machine.float_nondet {
+            return F2::NAN;
+        }
+
         /// Make the given NaN a signaling NaN.
         /// Returns `None` if this would not result in a NaN.
         fn make_signaling<F: Float>(f: F) -> Option<F> {
@@ -89,7 +94,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             if f.is_nan() { Some(f) } else { None }
         }
 
-        let this = self.eval_context_ref();
         let mut rand = this.machine.rng.borrow_mut();
         // Assemble an iterator of possible NaNs: preferred, quieting propagation, unchanged propagation.
         // On some targets there are more possibilities; for now we just generate those options that
@@ -118,6 +122,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
 
     fn equal_float_min_max<F: Float>(&self, a: F, b: F) -> F {
         let this = self.eval_context_ref();
+        if !this.machine.float_nondet {
+            return a;
+        }
         // Return one side non-deterministically.
         let mut rand = this.machine.rng.borrow_mut();
         if rand.random() { a } else { b }
diff --git a/src/tools/rust-analyzer/.github/workflows/autopublish.yaml b/src/tools/rust-analyzer/.github/workflows/autopublish.yaml
index d3f0499e434..6e2be7fd3da 100644
--- a/src/tools/rust-analyzer/.github/workflows/autopublish.yaml
+++ b/src/tools/rust-analyzer/.github/workflows/autopublish.yaml
@@ -28,7 +28,7 @@ jobs:
         run: rustup update --no-self-update stable
 
       - name: Install cargo-workspaces
-        run: cargo install cargo-workspaces
+        run: cargo install cargo-workspaces --version "0.3.6"
 
       - name: Publish Crates
         env:
@@ -54,8 +54,8 @@ jobs:
           cargo workspaces rename --from project-model project_model
           cargo workspaces rename --from test-fixture test_fixture
           cargo workspaces rename --from test-utils test_utils
-          # Remove library crates from the workspaces so we don't auto-publish them as well
-          sed -i 's/ "lib\/\*",//' ./Cargo.toml
+          # Remove library crates and xtask from the workspaces so we don't auto-publish them as well
+          sed -i 's|^members = .*$|members = ["crates/*"]|' Cargo.toml
           cargo workspaces rename ra_ap_%n
           find crates/rust-analyzer -type f -name '*.rs' -exec sed -i 's/rust_analyzer/ra_ap_rust_analyzer/g' {} +
           cargo workspaces publish --yes --force '*' --exact --no-git-commit --allow-dirty --skip-published custom 0.0.$(($RUN_NUMBER + 133))
diff --git a/src/tools/rust-analyzer/.github/workflows/publish-libs.yaml b/src/tools/rust-analyzer/.github/workflows/publish-libs.yaml
index 93ae5675a71..f2c8b6365b6 100644
--- a/src/tools/rust-analyzer/.github/workflows/publish-libs.yaml
+++ b/src/tools/rust-analyzer/.github/workflows/publish-libs.yaml
@@ -22,7 +22,7 @@ jobs:
         run: rustup update --no-self-update stable
 
       - name: Install cargo-workspaces
-        run: cargo install cargo-workspaces
+        run: cargo install cargo-workspaces --version "0.3.6"
 
       - name: Publish Crates
         env:
diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml
index 8c507189846..975fe277b22 100644
--- a/src/tools/rust-analyzer/Cargo.toml
+++ b/src/tools/rust-analyzer/Cargo.toml
@@ -21,6 +21,8 @@ smol_str.opt-level = 3
 text-size.opt-level = 3
 serde.opt-level = 3
 salsa.opt-level = 3
+dissimilar.opt-level = 3
+
 # This speeds up `cargo xtask dist`.
 miniz_oxide.opt-level = 3
 
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/dyn_map.rs b/src/tools/rust-analyzer/crates/hir-def/src/dyn_map.rs
index eed1490a7af..20018b61e5c 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/dyn_map.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/dyn_map.rs
@@ -67,8 +67,14 @@ pub mod keys {
     pub const PROC_MACRO: Key<ast::Fn, ProcMacroId> = Key::new();
     pub const MACRO_CALL: Key<ast::MacroCall, MacroCallId> = Key::new();
     pub const ATTR_MACRO_CALL: Key<ast::Item, MacroCallId> = Key::new();
-    pub const DERIVE_MACRO_CALL: Key<ast::Attr, (AttrId, MacroCallId, Box<[Option<MacroCallId>]>)> =
-        Key::new();
+    pub const DERIVE_MACRO_CALL: Key<
+        ast::Attr,
+        (
+            AttrId,
+            /* derive() */ MacroCallId,
+            /* actual derive macros */ Box<[Option<MacroCallId>]>,
+        ),
+    > = Key::new();
 
     /// XXX: AST Nodes and SyntaxNodes have identity equality semantics: nodes are
     /// equal if they point to exactly the same object.
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 cb4fcd887d8..2cc3ca8c752 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
@@ -931,11 +931,12 @@ pub fn new() {
 //                             PATH_TYPE@23..26
 //                               PATH@23..26
 //                                 PATH_SEGMENT@23..26
-//                                   L_ANGLE@23..24 "<"
-//                                   PAREN_TYPE@24..26
-//                                     L_PAREN@24..25 "("
-//                                     ERROR@25..26
-//                                       INT_NUMBER@25..26 "8"
+//                                   TYPE_ANCHOR@23..26
+//                                     L_ANGLE@23..24 "<"
+//                                     PAREN_TYPE@24..26
+//                                       L_PAREN@24..25 "("
+//                                       ERROR@25..26
+//                                         INT_NUMBER@25..26 "8"
 //                           PLUS@26..27 "+"
 //                     CONST_ARG@27..28
 //                       LITERAL@27..28
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 980ee264b02..1e985dc604e 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs
@@ -31,6 +31,7 @@ use crate::{
 #[query_group::query_group]
 pub trait HirDatabase: DefDatabase + std::fmt::Debug {
     #[salsa::invoke(crate::infer::infer_query)]
+    #[salsa::cycle(cycle_result = crate::infer::infer_cycle_result)]
     fn infer(&self, def: DefWithBodyId) -> Arc<InferenceResult>;
 
     // region:mir
@@ -132,6 +133,7 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug {
 
     // FIXME: Make this a non-interned query.
     #[salsa::invoke_interned(crate::lower::const_param_ty_with_diagnostics_query)]
+    #[salsa::cycle(cycle_result = crate::lower::const_param_ty_with_diagnostics_cycle_result)]
     fn const_param_ty_with_diagnostics(&self, def: ConstParamId) -> (Ty, Diagnostics);
 
     #[salsa::invoke(crate::lower::const_param_ty_query)]
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 e698fb201cb..14eb7160753 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
@@ -35,7 +35,8 @@ use chalk_ir::{
 use either::Either;
 use hir_def::{
     AdtId, AssocItemId, ConstId, DefWithBodyId, FieldId, FunctionId, GenericDefId, GenericParamId,
-    ImplId, ItemContainerId, Lookup, TraitId, TupleFieldId, TupleId, TypeAliasId, VariantId,
+    ImplId, ItemContainerId, LocalFieldId, Lookup, TraitId, TupleFieldId, TupleId, TypeAliasId,
+    VariantId,
     builtin_type::{BuiltinInt, BuiltinType, BuiltinUint},
     expr_store::{Body, ExpressionStore, HygieneId, path::Path},
     hir::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, LabelId, PatId},
@@ -135,6 +136,10 @@ pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<Infer
     Arc::new(ctx.resolve_all())
 }
 
+pub(crate) fn infer_cycle_result(_: &dyn HirDatabase, _: DefWithBodyId) -> Arc<InferenceResult> {
+    Arc::new(InferenceResult { has_errors: true, ..Default::default() })
+}
+
 /// Fully normalize all the types found within `ty` in context of `owner` body definition.
 ///
 /// This is appropriate to use only after type-check: it assumes
@@ -203,7 +208,7 @@ pub(crate) type InferResult<T> = Result<InferOk<T>, TypeError>;
 pub enum InferenceDiagnostic {
     NoSuchField {
         field: ExprOrPatId,
-        private: bool,
+        private: Option<LocalFieldId>,
         variant: VariantId,
     },
     PrivateField {
@@ -558,6 +563,9 @@ impl InferenceResult {
             ExprOrPatId::PatId(id) => self.type_of_pat.get(id),
         }
     }
+    pub fn is_erroneous(&self) -> bool {
+        self.has_errors && self.type_of_expr.iter().count() == 0
+    }
 }
 
 impl Index<ExprId> for InferenceResult {
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 87b7f3406ff..64031279296 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
@@ -554,7 +554,7 @@ impl InferenceContext<'_> {
                                             self.push_diagnostic(
                                                 InferenceDiagnostic::NoSuchField {
                                                     field: field.expr.into(),
-                                                    private: true,
+                                                    private: Some(local_id),
                                                     variant: def,
                                                 },
                                             );
@@ -564,7 +564,7 @@ impl InferenceContext<'_> {
                                     None => {
                                         self.push_diagnostic(InferenceDiagnostic::NoSuchField {
                                             field: field.expr.into(),
-                                            private: false,
+                                            private: None,
                                             variant: def,
                                         });
                                         None
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 a9a3265858e..4bc3e167ebf 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
@@ -143,7 +143,7 @@ impl InferenceContext<'_> {
                                 {
                                     self.push_diagnostic(InferenceDiagnostic::NoSuchField {
                                         field: inner.into(),
-                                        private: true,
+                                        private: Some(local_id),
                                         variant: def,
                                     });
                                 }
@@ -157,7 +157,7 @@ impl InferenceContext<'_> {
                             None => {
                                 self.push_diagnostic(InferenceDiagnostic::NoSuchField {
                                     field: inner.into(),
-                                    private: false,
+                                    private: None,
                                     variant: def,
                                 });
                                 self.err_ty()
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 ea8e7cc2be9..0a546768dab 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs
@@ -1604,6 +1604,14 @@ pub(crate) fn impl_self_ty_with_diagnostics_query(
     )
 }
 
+pub(crate) fn impl_self_ty_with_diagnostics_cycle_result(
+    db: &dyn HirDatabase,
+    impl_id: ImplId,
+) -> (Binders<Ty>, Diagnostics) {
+    let generics = generics(db, impl_id.into());
+    (make_binders(db, &generics, TyKind::Error.intern(Interner)), None)
+}
+
 pub(crate) fn const_param_ty_query(db: &dyn HirDatabase, def: ConstParamId) -> Ty {
     db.const_param_ty_with_diagnostics(def).0
 }
@@ -1633,12 +1641,12 @@ pub(crate) fn const_param_ty_with_diagnostics_query(
     (ty, create_diagnostics(ctx.diagnostics))
 }
 
-pub(crate) fn impl_self_ty_with_diagnostics_cycle_result(
-    db: &dyn HirDatabase,
-    impl_id: ImplId,
-) -> (Binders<Ty>, Diagnostics) {
-    let generics = generics(db, impl_id.into());
-    (make_binders(db, &generics, TyKind::Error.intern(Interner)), None)
+pub(crate) fn const_param_ty_with_diagnostics_cycle_result(
+    _: &dyn HirDatabase,
+    _: crate::db::HirDatabaseData,
+    _: ConstParamId,
+) -> (Ty, Diagnostics) {
+    (TyKind::Error.intern(Interner), None)
 }
 
 pub(crate) fn impl_trait_query(db: &dyn HirDatabase, impl_id: ImplId) -> Option<Binders<TraitRef>> {
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
index e6caf2d8d97..99d93515303 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs
@@ -2182,7 +2182,7 @@ pub fn lower_to_mir(
     // need to take this input explicitly.
     root_expr: ExprId,
 ) -> Result<MirBody> {
-    if infer.type_mismatches().next().is_some() {
+    if infer.type_mismatches().next().is_some() || infer.is_erroneous() {
         return Err(MirLowerError::HasErrors);
     }
     let mut ctx = MirLowerCtx::new(db, owner, body, infer);
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs
index 48474d2d26d..e8e3812c69d 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs
@@ -106,3 +106,256 @@ fn baz() -> i32 {
         assert_eq!(format!("{events:?}").matches("infer_shim").count(), 1, "{events:#?}")
     }
 }
+
+#[test]
+fn adding_struct_invalidates_infer() {
+    let (mut db, pos) = TestDB::with_position(
+        "
+//- /lib.rs
+fn foo() -> i32 {
+    1 + 1
+}
+
+fn bar() -> f32 {
+    2.0 * 3.0
+}
+$0",
+    );
+    {
+        let events = db.log_executed(|| {
+            let module = db.module_for_file(pos.file_id.file_id(&db));
+            let _crate_def_map = module.def_map(&db);
+            db.trait_impls_in_crate(module.krate());
+        });
+        assert!(format!("{events:?}").contains("trait_impls_in_crate_shim"))
+    }
+
+    let new_text = "
+fn foo() -> i32 {
+    1 + 1
+}
+
+fn bar() -> f32 {
+    2.0 * 3.0
+}
+
+pub struct NewStruct {
+    field: i32,
+}
+";
+
+    db.set_file_text(pos.file_id.file_id(&db), new_text);
+
+    {
+        let actual = db.log_executed(|| {
+            let module = db.module_for_file(pos.file_id.file_id(&db));
+            let _crate_def_map = module.def_map(&db);
+            db.trait_impls_in_crate(module.krate());
+        });
+
+        let expected = vec![
+            "parse_shim".to_owned(),
+            "ast_id_map_shim".to_owned(),
+            "file_item_tree_shim".to_owned(),
+            "real_span_map_shim".to_owned(),
+            "crate_local_def_map".to_owned(),
+            "trait_impls_in_crate_shim".to_owned(),
+        ];
+
+        assert_eq!(expected, actual);
+    }
+}
+
+#[test]
+fn adding_enum_query_log() {
+    let (mut db, pos) = TestDB::with_position(
+        "
+//- /lib.rs
+fn foo() -> i32 {
+    1 + 1
+}
+
+fn bar() -> f32 {
+    2.0 * 3.0
+}
+$0",
+    );
+    {
+        let events = db.log_executed(|| {
+            let module = db.module_for_file(pos.file_id.file_id(&db));
+            let _crate_def_map = module.def_map(&db);
+            db.trait_impls_in_crate(module.krate());
+        });
+        assert!(format!("{events:?}").contains("trait_impls_in_crate_shim"))
+    }
+
+    let new_text = "
+fn foo() -> i32 {
+    1 + 1
+}
+
+fn bar() -> f32 {
+    2.0 * 3.0
+}
+
+pub enum SomeEnum {
+    A,
+    B
+}
+";
+
+    db.set_file_text(pos.file_id.file_id(&db), new_text);
+
+    {
+        let actual = db.log_executed(|| {
+            let module = db.module_for_file(pos.file_id.file_id(&db));
+            let _crate_def_map = module.def_map(&db);
+            db.trait_impls_in_crate(module.krate());
+        });
+
+        let expected = vec![
+            "parse_shim".to_owned(),
+            "ast_id_map_shim".to_owned(),
+            "file_item_tree_shim".to_owned(),
+            "real_span_map_shim".to_owned(),
+            "crate_local_def_map".to_owned(),
+            "trait_impls_in_crate_shim".to_owned(),
+        ];
+
+        assert_eq!(expected, actual);
+    }
+}
+
+#[test]
+fn adding_use_query_log() {
+    let (mut db, pos) = TestDB::with_position(
+        "
+//- /lib.rs
+fn foo() -> i32 {
+    1 + 1
+}
+
+fn bar() -> f32 {
+    2.0 * 3.0
+}
+$0",
+    );
+    {
+        let events = db.log_executed(|| {
+            let module = db.module_for_file(pos.file_id.file_id(&db));
+            let _crate_def_map = module.def_map(&db);
+            db.trait_impls_in_crate(module.krate());
+        });
+        assert!(format!("{events:?}").contains("trait_impls_in_crate_shim"))
+    }
+
+    let new_text = "
+use std::collections::HashMap;
+
+fn foo() -> i32 {
+    1 + 1
+}
+
+fn bar() -> f32 {
+    2.0 * 3.0
+}
+";
+
+    db.set_file_text(pos.file_id.file_id(&db), new_text);
+
+    {
+        let actual = db.log_executed(|| {
+            let module = db.module_for_file(pos.file_id.file_id(&db));
+            let _crate_def_map = module.def_map(&db);
+            db.trait_impls_in_crate(module.krate());
+        });
+
+        let expected = vec![
+            "parse_shim".to_owned(),
+            "ast_id_map_shim".to_owned(),
+            "file_item_tree_shim".to_owned(),
+            "real_span_map_shim".to_owned(),
+            "crate_local_def_map".to_owned(),
+            "trait_impls_in_crate_shim".to_owned(),
+        ];
+
+        assert_eq!(expected, actual);
+    }
+}
+
+#[test]
+fn adding_impl_query_log() {
+    let (mut db, pos) = TestDB::with_position(
+        "
+//- /lib.rs
+fn foo() -> i32 {
+    1 + 1
+}
+
+fn bar() -> f32 {
+    2.0 * 3.0
+}
+
+pub struct SomeStruct {
+    field: i32,
+}
+$0",
+    );
+    {
+        let events = db.log_executed(|| {
+            let module = db.module_for_file(pos.file_id.file_id(&db));
+            let _crate_def_map = module.def_map(&db);
+            db.trait_impls_in_crate(module.krate());
+        });
+        assert!(format!("{events:?}").contains("trait_impls_in_crate_shim"))
+    }
+
+    let new_text = "
+fn foo() -> i32 {
+    1 + 1
+}
+
+fn bar() -> f32 {
+    2.0 * 3.0
+}
+
+pub struct SomeStruct {
+    field: i32,
+}
+
+impl SomeStruct {
+    pub fn new(value: i32) -> Self {
+        Self { field: value }
+    }
+}
+";
+
+    db.set_file_text(pos.file_id.file_id(&db), new_text);
+
+    {
+        let actual = db.log_executed(|| {
+            let module = db.module_for_file(pos.file_id.file_id(&db));
+            let _crate_def_map = module.def_map(&db);
+            db.trait_impls_in_crate(module.krate());
+        });
+
+        let expected = vec![
+            "parse_shim".to_owned(),
+            "ast_id_map_shim".to_owned(),
+            "file_item_tree_shim".to_owned(),
+            "real_span_map_shim".to_owned(),
+            "crate_local_def_map".to_owned(),
+            "trait_impls_in_crate_shim".to_owned(),
+            "attrs_shim".to_owned(),
+            "impl_trait_with_diagnostics_shim".to_owned(),
+            "impl_signature_shim".to_owned(),
+            "impl_signature_with_source_map_shim".to_owned(),
+            "impl_self_ty_with_diagnostics_shim".to_owned(),
+            "struct_signature_shim".to_owned(),
+            "struct_signature_with_source_map_shim".to_owned(),
+            "type_for_adt_tracked".to_owned(),
+        ];
+
+        assert_eq!(expected, actual);
+    }
+}
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 47c695c6974..ff8adeef1db 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
@@ -2301,3 +2301,51 @@ trait Foo {
         "#]],
     );
 }
+
+#[test]
+fn no_panic_on_recursive_const() {
+    check_infer(
+        r#"
+struct Foo<const N: usize> {}
+impl<const N: Foo<N>> Foo<N> {
+    fn foo(self) {}
+}
+
+fn test() {
+    let _ = N;
+}
+"#,
+        expect![[r#"
+            72..76 'self': Foo<N>
+            78..80 '{}': ()
+            94..112 '{     ...= N; }': ()
+            104..105 '_': {unknown}
+            108..109 'N': {unknown}
+        "#]],
+    );
+
+    check_infer(
+        r#"
+struct Foo<const N: usize>;
+const N: Foo<N> = Foo;
+
+impl<const N: usize> Foo<N> {
+    fn foo(self) -> usize {
+        N
+    }
+}
+
+fn test() {
+    let _ = N;
+}
+"#,
+        expect![[r#"
+            93..97 'self': Foo<N>
+            108..125 '{     ...     }': usize
+            118..119 'N': usize
+            139..157 '{     ...= N; }': ()
+            149..150 '_': Foo<_>
+            153..154 'N': Foo<_>
+        "#]],
+    );
+}
diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs
index b6e3002ed5d..f7b140e03d4 100644
--- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs
@@ -224,7 +224,7 @@ pub struct MalformedDerive {
 #[derive(Debug)]
 pub struct NoSuchField {
     pub field: InFile<AstPtr<Either<ast::RecordExprField, ast::RecordPatField>>>,
-    pub private: bool,
+    pub private: Option<Field>,
     pub variant: VariantId,
 }
 
@@ -648,6 +648,7 @@ impl AnyDiagnostic {
                     }
                     ExprOrPatId::PatId(pat) => source_map.pat_field_syntax(pat),
                 };
+                let private = private.map(|id| Field { id, parent: variant.into() });
                 NoSuchField { field: expr_or_pat, private, variant }.into()
             }
             &InferenceDiagnostic::MismatchedArgCount { call_expr, expected, found } => {
diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs
index e01774650b1..4a2e8e379fb 100644
--- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs
@@ -222,6 +222,21 @@ impl<DB: HirDatabase> Semantics<'_, DB> {
         self.imp.descend_node_at_offset(node, offset).filter_map(|mut it| it.find_map(N::cast))
     }
 
+    // FIXME: Rethink this API
+    pub fn find_namelike_at_offset_with_descend<'slf>(
+        &'slf self,
+        node: &SyntaxNode,
+        offset: TextSize,
+    ) -> impl Iterator<Item = ast::NameLike> + 'slf {
+        node.token_at_offset(offset)
+            .map(move |token| self.descend_into_macros_no_opaque(token))
+            .map(|descendants| descendants.into_iter().filter_map(move |it| it.value.parent()))
+            // re-order the tokens from token_at_offset by returning the ancestors with the smaller first nodes first
+            // See algo::ancestors_at_offset, which uses the same approach
+            .kmerge_by(|left, right| left.text_range().len().lt(&right.text_range().len()))
+            .filter_map(ast::NameLike::cast)
+    }
+
     pub fn resolve_range_pat(&self, range_pat: &ast::RangePat) -> Option<Struct> {
         self.imp.resolve_range_pat(range_pat).map(Struct::from)
     }
@@ -535,7 +550,7 @@ impl<'db> SemanticsImpl<'db> {
     }
 
     pub fn is_derive_annotated(&self, adt: InFile<&ast::Adt>) -> bool {
-        self.with_ctx(|ctx| ctx.has_derives(adt))
+        self.with_ctx(|ctx| ctx.file_of_adt_has_derives(adt))
     }
 
     pub fn derive_helpers_in_scope(&self, adt: &ast::Adt) -> Option<Vec<(Symbol, Symbol)>> {
@@ -644,7 +659,7 @@ impl<'db> SemanticsImpl<'db> {
 
     /// Checks if renaming `renamed` to `new_name` may introduce conflicts with other locals,
     /// and returns the conflicting locals.
-    pub fn rename_conflicts(&self, to_be_renamed: &Local, new_name: &str) -> Vec<Local> {
+    pub fn rename_conflicts(&self, to_be_renamed: &Local, new_name: &Name) -> Vec<Local> {
         let body = self.db.body(to_be_renamed.parent);
         let resolver = to_be_renamed.parent.resolver(self.db);
         let starting_expr =
@@ -653,7 +668,7 @@ impl<'db> SemanticsImpl<'db> {
             body: &body,
             conflicts: FxHashSet::default(),
             db: self.db,
-            new_name: Symbol::intern(new_name),
+            new_name: new_name.symbol().clone(),
             old_name: to_be_renamed.name(self.db).symbol().clone(),
             owner: to_be_renamed.parent,
             to_be_renamed: to_be_renamed.binding_id,
@@ -877,8 +892,9 @@ impl<'db> SemanticsImpl<'db> {
 
         if first == last {
             // node is just the token, so descend the token
-            self.descend_into_macros_impl(
+            self.descend_into_macros_all(
                 InFile::new(file.file_id, first),
+                false,
                 &mut |InFile { value, .. }, _ctx| {
                     if let Some(node) = value
                         .parent_ancestors()
@@ -887,20 +903,21 @@ impl<'db> SemanticsImpl<'db> {
                     {
                         res.push(node)
                     }
-                    CONTINUE_NO_BREAKS
                 },
             );
         } else {
             // Descend first and last token, then zip them to look for the node they belong to
             let mut scratch: SmallVec<[_; 1]> = smallvec![];
-            self.descend_into_macros_impl(InFile::new(file.file_id, first), &mut |token, _ctx| {
-                scratch.push(token);
-                CONTINUE_NO_BREAKS
-            });
+            self.descend_into_macros_all(
+                InFile::new(file.file_id, first),
+                false,
+                &mut |token, _ctx| scratch.push(token),
+            );
 
             let mut scratch = scratch.into_iter();
-            self.descend_into_macros_impl(
+            self.descend_into_macros_all(
                 InFile::new(file.file_id, last),
+                false,
                 &mut |InFile { value: last, file_id: last_fid }, _ctx| {
                     if let Some(InFile { value: first, file_id: first_fid }) = scratch.next() {
                         if first_fid == last_fid {
@@ -917,17 +934,18 @@ impl<'db> SemanticsImpl<'db> {
                             }
                         }
                     }
-                    CONTINUE_NO_BREAKS
                 },
             );
         }
         res
     }
 
-    pub fn is_inside_macro_call(&self, token: InFile<&SyntaxToken>) -> bool {
-        // FIXME: Maybe `ancestors_with_macros()` is more suitable here? Currently
-        // this is only used on real (not macro) files so this is not a problem.
-        token.value.parent_ancestors().any(|ancestor| {
+    /// Returns true if the given input is within a macro call.
+    ///
+    /// Note that if this token itself is within the context of a macro expansion does not matter.
+    /// That is, we strictly check if it lies inside the input of a macro call.
+    pub fn is_inside_macro_call(&self, token @ InFile { value, .. }: InFile<&SyntaxToken>) -> bool {
+        value.parent_ancestors().any(|ancestor| {
             if ast::MacroCall::can_cast(ancestor.kind()) {
                 return true;
             }
@@ -952,7 +970,7 @@ impl<'db> SemanticsImpl<'db> {
                     ast::Item::Union(it) => it.into(),
                     _ => return false,
                 };
-                ctx.has_derives(token.with_value(&adt))
+                ctx.file_of_adt_has_derives(token.with_value(&adt))
             })
         })
     }
@@ -962,18 +980,18 @@ impl<'db> SemanticsImpl<'db> {
         token: SyntaxToken,
         mut cb: impl FnMut(InFile<SyntaxToken>, SyntaxContext),
     ) {
-        self.descend_into_macros_impl(self.wrap_token_infile(token), &mut |t, ctx| {
-            cb(t, ctx);
-            CONTINUE_NO_BREAKS
+        self.descend_into_macros_all(self.wrap_token_infile(token), false, &mut |t, ctx| {
+            cb(t, ctx)
         });
     }
 
     pub fn descend_into_macros(&self, token: SyntaxToken) -> SmallVec<[SyntaxToken; 1]> {
         let mut res = smallvec![];
-        self.descend_into_macros_impl(self.wrap_token_infile(token.clone()), &mut |t, _ctx| {
-            res.push(t.value);
-            CONTINUE_NO_BREAKS
-        });
+        self.descend_into_macros_all(
+            self.wrap_token_infile(token.clone()),
+            false,
+            &mut |t, _ctx| res.push(t.value),
+        );
         if res.is_empty() {
             res.push(token);
         }
@@ -986,12 +1004,11 @@ impl<'db> SemanticsImpl<'db> {
     ) -> SmallVec<[InFile<SyntaxToken>; 1]> {
         let mut res = smallvec![];
         let token = self.wrap_token_infile(token);
-        self.descend_into_macros_impl(token.clone(), &mut |t, ctx| {
+        self.descend_into_macros_all(token.clone(), true, &mut |t, ctx| {
             if !ctx.is_opaque(self.db) {
                 // Don't descend into opaque contexts
                 res.push(t);
             }
-            CONTINUE_NO_BREAKS
         });
         if res.is_empty() {
             res.push(token);
@@ -1004,7 +1021,7 @@ impl<'db> SemanticsImpl<'db> {
         token: InFile<SyntaxToken>,
         mut cb: impl FnMut(InFile<SyntaxToken>, SyntaxContext) -> ControlFlow<T>,
     ) -> Option<T> {
-        self.descend_into_macros_impl(token, &mut cb)
+        self.descend_into_macros_impl(token, false, &mut cb)
     }
 
     /// Descends the token into expansions, returning the tokens that matches the input
@@ -1074,44 +1091,56 @@ impl<'db> SemanticsImpl<'db> {
         .unwrap_or(token)
     }
 
+    fn descend_into_macros_all(
+        &self,
+        token: InFile<SyntaxToken>,
+        always_descend_into_derives: bool,
+        f: &mut dyn FnMut(InFile<SyntaxToken>, SyntaxContext),
+    ) {
+        self.descend_into_macros_impl(token, always_descend_into_derives, &mut |tok, ctx| {
+            f(tok, ctx);
+            CONTINUE_NO_BREAKS
+        });
+    }
+
     fn descend_into_macros_impl<T>(
         &self,
         InFile { value: token, file_id }: InFile<SyntaxToken>,
+        always_descend_into_derives: bool,
         f: &mut dyn FnMut(InFile<SyntaxToken>, SyntaxContext) -> ControlFlow<T>,
     ) -> Option<T> {
         let _p = tracing::info_span!("descend_into_macros_impl").entered();
 
-        let span = self.db.span_map(file_id).span_for_range(token.text_range());
+        let db = self.db;
+        let span = db.span_map(file_id).span_for_range(token.text_range());
 
         // Process the expansion of a call, pushing all tokens with our span in the expansion back onto our stack
-        let process_expansion_for_token = |stack: &mut Vec<_>, macro_file| {
-            let InMacroFile { file_id, value: mapped_tokens } = self.with_ctx(|ctx| {
-                Some(
-                    ctx.cache
-                        .get_or_insert_expansion(ctx.db, macro_file)
-                        .map_range_down(span)?
-                        .map(SmallVec::<[_; 2]>::from_iter),
-                )
-            })?;
-            // we have found a mapping for the token if the vec is non-empty
-            let res = mapped_tokens.is_empty().not().then_some(());
-            // requeue the tokens we got from mapping our current token down
-            stack.push((HirFileId::from(file_id), mapped_tokens));
-            res
-        };
+        let process_expansion_for_token =
+            |ctx: &mut SourceToDefCtx<'_, '_>, stack: &mut Vec<_>, macro_file| {
+                let InMacroFile { file_id, value: mapped_tokens } = ctx
+                    .cache
+                    .get_or_insert_expansion(ctx.db, macro_file)
+                    .map_range_down(span)?
+                    .map(SmallVec::<[_; 2]>::from_iter);
+                // we have found a mapping for the token if the vec is non-empty
+                let res = mapped_tokens.is_empty().not().then_some(());
+                // requeue the tokens we got from mapping our current token down
+                stack.push((HirFileId::from(file_id), mapped_tokens));
+                res
+            };
 
         // A stack of tokens to process, along with the file they came from
         // These are tracked to know which macro calls we still have to look into
         // the tokens themselves aren't that interesting as the span that is being used to map
         // things down never changes.
         let mut stack: Vec<(_, SmallVec<[_; 2]>)> = vec![];
-        let include = file_id.file_id().and_then(|file_id| {
-            self.s2d_cache.borrow_mut().get_or_insert_include_for(self.db, file_id)
-        });
+        let include = file_id
+            .file_id()
+            .and_then(|file_id| self.s2d_cache.borrow_mut().get_or_insert_include_for(db, file_id));
         match include {
             Some(include) => {
                 // include! inputs are always from real files, so they only need to be handled once upfront
-                process_expansion_for_token(&mut stack, include)?;
+                self.with_ctx(|ctx| process_expansion_for_token(ctx, &mut stack, include))?;
             }
             None => {
                 stack.push((file_id, smallvec![(token, span.ctx)]));
@@ -1133,62 +1162,120 @@ impl<'db> SemanticsImpl<'db> {
             tokens.reverse();
             while let Some((token, ctx)) = tokens.pop() {
                 let was_not_remapped = (|| {
-                    // First expand into attribute invocations
-                    let containing_attribute_macro_call = self.with_ctx(|ctx| {
-                        token.parent_ancestors().filter_map(ast::Item::cast).find_map(|item| {
-                            // Don't force populate the dyn cache for items that don't have an attribute anyways
-                            item.attrs().next()?;
-                            Some((ctx.item_to_macro_call(InFile::new(expansion, &item))?, item))
-                        })
-                    });
-                    if let Some((call_id, item)) = containing_attribute_macro_call {
-                        let attr_id = match self.db.lookup_intern_macro_call(call_id).kind {
-                            hir_expand::MacroCallKind::Attr { invoc_attr_index, .. } => {
-                                invoc_attr_index.ast_index()
-                            }
-                            _ => 0,
-                        };
-                        // FIXME: here, the attribute's text range is used to strip away all
-                        // entries from the start of the attribute "list" up the invoking
-                        // attribute. But in
-                        // ```
-                        // mod foo {
-                        //     #![inner]
-                        // }
-                        // ```
-                        // we don't wanna strip away stuff in the `mod foo {` range, that is
-                        // here if the id corresponds to an inner attribute we got strip all
-                        // text ranges of the outer ones, and then all of the inner ones up
-                        // to the invoking attribute so that the inbetween is ignored.
-                        let text_range = item.syntax().text_range();
-                        let start = collect_attrs(&item)
-                            .nth(attr_id)
-                            .map(|attr| match attr.1 {
-                                Either::Left(it) => it.syntax().text_range().start(),
-                                Either::Right(it) => it.syntax().text_range().start(),
+                    // First expand into attribute invocations, this is required to be handled
+                    // upfront as any other macro call within will not semantically resolve unless
+                    // also descended.
+                    let res = self.with_ctx(|ctx| {
+                        token
+                            .parent_ancestors()
+                            .filter_map(ast::Item::cast)
+                            // FIXME: This might work incorrectly when we have a derive, followed by
+                            // an attribute on an item, like:
+                            // ```
+                            // #[derive(Debug$0)]
+                            // #[my_attr]
+                            // struct MyStruct;
+                            // ```
+                            // here we should not consider the attribute at all, as our cursor
+                            // technically lies outside of its expansion
+                            .find_map(|item| {
+                                // Don't force populate the dyn cache for items that don't have an attribute anyways
+                                item.attrs().next()?;
+                                ctx.item_to_macro_call(InFile::new(expansion, &item))
+                                    .zip(Some(item))
                             })
-                            .unwrap_or_else(|| text_range.start());
-                        let text_range = TextRange::new(start, text_range.end());
-                        filter_duplicates(tokens, text_range);
-                        return process_expansion_for_token(&mut stack, call_id);
+                            .map(|(call_id, item)| {
+                                let attr_id = match db.lookup_intern_macro_call(call_id).kind {
+                                    hir_expand::MacroCallKind::Attr {
+                                        invoc_attr_index, ..
+                                    } => invoc_attr_index.ast_index(),
+                                    _ => 0,
+                                };
+                                // FIXME: here, the attribute's text range is used to strip away all
+                                // entries from the start of the attribute "list" up the invoking
+                                // attribute. But in
+                                // ```
+                                // mod foo {
+                                //     #![inner]
+                                // }
+                                // ```
+                                // we don't wanna strip away stuff in the `mod foo {` range, that is
+                                // here if the id corresponds to an inner attribute we got strip all
+                                // text ranges of the outer ones, and then all of the inner ones up
+                                // to the invoking attribute so that the inbetween is ignored.
+                                let text_range = item.syntax().text_range();
+                                let start = collect_attrs(&item)
+                                    .nth(attr_id)
+                                    .map(|attr| match attr.1 {
+                                        Either::Left(it) => it.syntax().text_range().start(),
+                                        Either::Right(it) => it.syntax().text_range().start(),
+                                    })
+                                    .unwrap_or_else(|| text_range.start());
+                                let text_range = TextRange::new(start, text_range.end());
+                                filter_duplicates(tokens, text_range);
+                                process_expansion_for_token(ctx, &mut stack, call_id)
+                            })
+                    });
+
+                    if let Some(res) = res {
+                        return res;
                     }
 
+                    if always_descend_into_derives {
+                        let res = self.with_ctx(|ctx| {
+                            let (derives, adt) = token
+                                .parent_ancestors()
+                                .filter_map(ast::Adt::cast)
+                                .find_map(|adt| {
+                                    Some((
+                                        ctx.derive_macro_calls(InFile::new(expansion, &adt))?
+                                            .map(|(a, b, c)| (a, b, c.to_owned()))
+                                            .collect::<SmallVec<[_; 2]>>(),
+                                        adt,
+                                    ))
+                                })?;
+                            let mut res = None;
+                            for (_, derive_attr, derives) in derives {
+                                // as there may be multiple derives registering the same helper
+                                // name, we gotta make sure to call this for all of them!
+                                // FIXME: We need to call `f` for all of them as well though!
+                                res = res.or(process_expansion_for_token(
+                                    ctx,
+                                    &mut stack,
+                                    derive_attr,
+                                ));
+                                for derive in derives.into_iter().flatten() {
+                                    res = res
+                                        .or(process_expansion_for_token(ctx, &mut stack, derive));
+                                }
+                            }
+                            // remove all tokens that are within the derives expansion
+                            filter_duplicates(tokens, adt.syntax().text_range());
+                            Some(res)
+                        });
+                        // if we found derives, we can early exit. There is no way we can be in any
+                        // macro call at this point given we are not in a token tree
+                        if let Some(res) = res {
+                            return res;
+                        }
+                    }
                     // Then check for token trees, that means we are either in a function-like macro or
                     // secondary attribute inputs
                     let tt = token
                         .parent_ancestors()
                         .map_while(Either::<ast::TokenTree, ast::Meta>::cast)
                         .last()?;
+
                     match tt {
                         // function-like macro call
                         Either::Left(tt) => {
+                            let macro_call = tt.syntax().parent().and_then(ast::MacroCall::cast)?;
                             if tt.left_delimiter_token().map_or(false, |it| it == token) {
                                 return None;
                             }
                             if tt.right_delimiter_token().map_or(false, |it| it == token) {
                                 return None;
                             }
-                            let macro_call = tt.syntax().parent().and_then(ast::MacroCall::cast)?;
                             let mcall = InFile::new(expansion, macro_call);
                             let file_id = match m_cache.get(&mcall) {
                                 Some(&it) => it,
@@ -1201,13 +1288,16 @@ impl<'db> SemanticsImpl<'db> {
                             let text_range = tt.syntax().text_range();
                             filter_duplicates(tokens, text_range);
 
-                            process_expansion_for_token(&mut stack, file_id).or(file_id
-                                .eager_arg(self.db)
-                                .and_then(|arg| {
-                                    // also descend into eager expansions
-                                    process_expansion_for_token(&mut stack, arg)
-                                }))
+                            self.with_ctx(|ctx| {
+                                process_expansion_for_token(ctx, &mut stack, file_id).or(file_id
+                                    .eager_arg(db)
+                                    .and_then(|arg| {
+                                        // also descend into eager expansions
+                                        process_expansion_for_token(ctx, &mut stack, arg)
+                                    }))
+                            })
                         }
+                        Either::Right(_) if always_descend_into_derives => None,
                         // derive or derive helper
                         Either::Right(meta) => {
                             // attribute we failed expansion for earlier, this might be a derive invocation
@@ -1216,31 +1306,33 @@ impl<'db> SemanticsImpl<'db> {
                             let adt = match attr.syntax().parent().and_then(ast::Adt::cast) {
                                 Some(adt) => {
                                     // this might be a derive on an ADT
-                                    let derive_call = self.with_ctx(|ctx| {
+                                    let res = self.with_ctx(|ctx| {
                                         // so try downmapping the token into the pseudo derive expansion
                                         // see [hir_expand::builtin_attr_macro] for how the pseudo derive expansion works
-                                        ctx.attr_to_derive_macro_call(
-                                            InFile::new(expansion, &adt),
-                                            InFile::new(expansion, attr.clone()),
-                                        )
-                                        .map(|(_, call_id, _)| call_id)
+                                        let derive_call = ctx
+                                            .attr_to_derive_macro_call(
+                                                InFile::new(expansion, &adt),
+                                                InFile::new(expansion, attr.clone()),
+                                            )?
+                                            .1;
+
+                                        // resolved to a derive
+                                        let text_range = attr.syntax().text_range();
+                                        // remove any other token in this macro input, all their mappings are the
+                                        // same as this
+                                        tokens.retain(|(t, _)| {
+                                            !text_range.contains_range(t.text_range())
+                                        });
+                                        Some(process_expansion_for_token(
+                                            ctx,
+                                            &mut stack,
+                                            derive_call,
+                                        ))
                                     });
-
-                                    match derive_call {
-                                        Some(call_id) => {
-                                            // resolved to a derive
-                                            let text_range = attr.syntax().text_range();
-                                            // remove any other token in this macro input, all their mappings are the
-                                            // same as this
-                                            tokens.retain(|(t, _)| {
-                                                !text_range.contains_range(t.text_range())
-                                            });
-                                            return process_expansion_for_token(
-                                                &mut stack, call_id,
-                                            );
-                                        }
-                                        None => Some(adt),
+                                    if let Some(res) = res {
+                                        return res;
                                     }
+                                    Some(adt)
                                 }
                                 None => {
                                     // Otherwise this could be a derive helper on a variant or field
@@ -1254,12 +1346,9 @@ impl<'db> SemanticsImpl<'db> {
                                     )
                                 }
                             }?;
-                            if !self.with_ctx(|ctx| ctx.has_derives(InFile::new(expansion, &adt))) {
-                                return None;
-                            }
                             let attr_name =
                                 attr.path().and_then(|it| it.as_single_name_ref())?.as_name();
-                            // Not an attribute, nor a derive, so it's either an intert attribute or a derive helper
+                            // Not an attribute, nor a derive, so it's either an inert attribute or a derive helper
                             // Try to resolve to a derive helper and downmap
                             let resolver = &token
                                 .parent()
@@ -1267,7 +1356,7 @@ impl<'db> SemanticsImpl<'db> {
                                     self.analyze_impl(InFile::new(expansion, &parent), None, false)
                                 })?
                                 .resolver;
-                            let id = self.db.ast_id_map(expansion).ast_id(&adt);
+                            let id = db.ast_id_map(expansion).ast_id(&adt);
                             let helpers = resolver
                                 .def_map()
                                 .derive_helpers_in_scope(InFile::new(expansion, id))?;
@@ -1278,20 +1367,22 @@ impl<'db> SemanticsImpl<'db> {
                             }
 
                             let mut res = None;
-                            for (.., derive) in
-                                helpers.iter().filter(|(helper, ..)| *helper == attr_name)
-                            {
-                                // as there may be multiple derives registering the same helper
-                                // name, we gotta make sure to call this for all of them!
-                                // FIXME: We need to call `f` for all of them as well though!
-                                res = res.or(process_expansion_for_token(&mut stack, *derive));
-                            }
-                            res
+                            self.with_ctx(|ctx| {
+                                for (.., derive) in
+                                    helpers.iter().filter(|(helper, ..)| *helper == attr_name)
+                                {
+                                    // as there may be multiple derives registering the same helper
+                                    // name, we gotta make sure to call this for all of them!
+                                    // FIXME: We need to call `f` for all of them as well though!
+                                    res = res
+                                        .or(process_expansion_for_token(ctx, &mut stack, *derive));
+                                }
+                                res
+                            })
                         }
                     }
                 })()
                 .is_none();
-
                 if was_not_remapped {
                     if let ControlFlow::Break(b) = f(InFile::new(expansion, token), ctx) {
                         return Some(b);
@@ -1380,25 +1471,31 @@ impl<'db> SemanticsImpl<'db> {
     }
 
     /// Iterates the ancestors of the given node, climbing up macro expansions while doing so.
+    // FIXME: Replace with `ancestors_with_macros_file` when all usages are updated.
     pub fn ancestors_with_macros(
         &self,
         node: SyntaxNode,
     ) -> impl Iterator<Item = SyntaxNode> + Clone + '_ {
         let node = self.find_file(&node);
-        iter::successors(Some(node.cloned()), move |&InFile { file_id, ref value }| {
-            match value.parent() {
-                Some(parent) => Some(InFile::new(file_id, parent)),
-                None => {
-                    let macro_file = file_id.macro_file()?;
-
-                    self.with_ctx(|ctx| {
-                        let expansion_info = ctx.cache.get_or_insert_expansion(ctx.db, macro_file);
-                        expansion_info.arg().map(|node| node?.parent()).transpose()
-                    })
-                }
+        self.ancestors_with_macros_file(node.cloned()).map(|it| it.value)
+    }
+
+    /// Iterates the ancestors of the given node, climbing up macro expansions while doing so.
+    pub fn ancestors_with_macros_file(
+        &self,
+        node: InFile<SyntaxNode>,
+    ) -> impl Iterator<Item = InFile<SyntaxNode>> + Clone + '_ {
+        iter::successors(Some(node), move |&InFile { file_id, ref value }| match value.parent() {
+            Some(parent) => Some(InFile::new(file_id, parent)),
+            None => {
+                let macro_file = file_id.macro_file()?;
+
+                self.with_ctx(|ctx| {
+                    let expansion_info = ctx.cache.get_or_insert_expansion(ctx.db, macro_file);
+                    expansion_info.arg().map(|node| node?.parent()).transpose()
+                })
             }
         })
-        .map(|it| it.value)
     }
 
     pub fn ancestors_at_offset_with_macros(
@@ -1851,18 +1948,12 @@ impl<'db> SemanticsImpl<'db> {
             ChildContainer::TraitId(it) => {
                 return Some(SourceAnalyzer::new_generic_def(self.db, it.into(), node, offset));
             }
-            ChildContainer::TraitAliasId(it) => {
-                return Some(SourceAnalyzer::new_generic_def(self.db, it.into(), node, offset));
-            }
             ChildContainer::ImplId(it) => {
                 return Some(SourceAnalyzer::new_generic_def(self.db, it.into(), node, offset));
             }
             ChildContainer::EnumId(it) => {
                 return Some(SourceAnalyzer::new_generic_def(self.db, it.into(), node, offset));
             }
-            ChildContainer::TypeAliasId(it) => {
-                return Some(SourceAnalyzer::new_generic_def(self.db, it.into(), node, offset));
-            }
             ChildContainer::GenericDefId(it) => {
                 return Some(SourceAnalyzer::new_generic_def(self.db, it, node, offset));
             }
diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs b/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs
index 6accf9b2e9c..1a6d63c88c6 100644
--- a/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs
@@ -17,6 +17,7 @@ use hir_def::{
         DynMap,
         keys::{self, Key},
     },
+    hir::generics::GenericParams,
     item_scope::ItemScope,
     item_tree::ItemTreeNode,
     nameres::DefMap,
@@ -49,6 +50,12 @@ impl ChildBySource for TraitId {
         data.items.iter().for_each(|&(_, item)| {
             add_assoc_item(db, res, file_id, item);
         });
+        let (_, source_map) = db.trait_signature_with_source_map(*self);
+        source_map.expansions().filter(|(ast, _)| ast.file_id == file_id).for_each(
+            |(ast, &exp_id)| {
+                res[keys::MACRO_CALL].insert(ast.value, exp_id);
+            },
+        );
     }
 }
 
@@ -68,6 +75,12 @@ impl ChildBySource for ImplId {
         data.items.iter().for_each(|&(_, item)| {
             add_assoc_item(db, res, file_id, item);
         });
+        let (_, source_map) = db.impl_signature_with_source_map(*self);
+        source_map.expansions().filter(|(ast, _)| ast.file_id == file_id).for_each(
+            |(ast, &exp_id)| {
+                res[keys::MACRO_CALL].insert(ast.value, exp_id);
+            },
+        );
     }
 }
 
@@ -178,6 +191,8 @@ impl ChildBySource for VariantId {
                 Either::Right(source) => res[keys::RECORD_FIELD].insert(AstPtr::new(&source), id),
             }
         }
+        let (_, sm) = db.variant_fields_with_source_map(*self);
+        sm.expansions().for_each(|(ast, &exp_id)| res[keys::MACRO_CALL].insert(ast.value, exp_id));
     }
 }
 
@@ -195,6 +210,11 @@ impl ChildBySource for EnumId {
             res[keys::ENUM_VARIANT]
                 .insert(ast_id_map.get(tree[variant.lookup(db).id.value].ast_id), variant);
         });
+        let (_, source_map) = db.enum_signature_with_source_map(*self);
+        source_map
+            .expansions()
+            .filter(|(ast, _)| ast.file_id == file_id)
+            .for_each(|(ast, &exp_id)| res[keys::MACRO_CALL].insert(ast.value, exp_id));
     }
 }
 
@@ -225,7 +245,8 @@ impl ChildBySource for GenericDefId {
             return;
         }
 
-        let generic_params = db.generic_params(*self);
+        let (generic_params, _, source_map) =
+            GenericParams::generic_params_and_store_and_source_map(db, *self);
         let mut toc_idx_iter = generic_params.iter_type_or_consts().map(|(idx, _)| idx);
         let lts_idx_iter = generic_params.iter_lt().map(|(idx, _)| idx);
 
@@ -253,6 +274,11 @@ impl ChildBySource for GenericDefId {
                 res[keys::LIFETIME_PARAM].insert(AstPtr::new(&ast_param), id);
             }
         }
+
+        source_map
+            .expansions()
+            .filter(|(ast, _)| ast.file_id == file_id)
+            .for_each(|(ast, &exp_id)| res[keys::MACRO_CALL].insert(ast.value, exp_id));
     }
 }
 
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 7f6c9af4740..71ee0f69389 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
@@ -108,7 +108,7 @@ use span::FileId;
 use stdx::impl_from;
 use syntax::{
     AstNode, AstPtr, SyntaxNode,
-    ast::{self, HasName},
+    ast::{self, HasAttrs, HasName},
 };
 use tt::TextRange;
 
@@ -411,10 +411,25 @@ impl SourceToDefCtx<'_, '_> {
             .map(|&(attr_id, call_id, ref ids)| (attr_id, call_id, &**ids))
     }
 
-    pub(super) fn has_derives(&mut self, adt: InFile<&ast::Adt>) -> bool {
+    // FIXME: Make this more fine grained! This should be a `adt_has_derives`!
+    pub(super) fn file_of_adt_has_derives(&mut self, adt: InFile<&ast::Adt>) -> bool {
         self.dyn_map(adt).as_ref().is_some_and(|map| !map[keys::DERIVE_MACRO_CALL].is_empty())
     }
 
+    pub(super) fn derive_macro_calls<'slf>(
+        &'slf mut self,
+        adt: InFile<&ast::Adt>,
+    ) -> Option<impl Iterator<Item = (AttrId, MacroCallId, &'slf [Option<MacroCallId>])> + use<'slf>>
+    {
+        self.dyn_map(adt).as_ref().map(|&map| {
+            let dyn_map = &map[keys::DERIVE_MACRO_CALL];
+            adt.value
+                .attrs()
+                .filter_map(move |attr| dyn_map.get(&AstPtr::new(&attr)))
+                .map(|&(attr_id, call_id, ref ids)| (attr_id, call_id, &**ids))
+        })
+    }
+
     fn to_def<Ast: AstNode + 'static, ID: Copy + 'static>(
         &mut self,
         src: InFile<&Ast>,
@@ -616,14 +631,14 @@ 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) => {
-                    self.type_alias_to_def(container.with_value(it))?.into()
-                }
+                ast::Item::TypeAlias(it) => ChildContainer::GenericDefId(
+                    self.type_alias_to_def(container.with_value(it))?.into(),
+                ),
+                ast::Item::TraitAlias(it) => ChildContainer::GenericDefId(
+                    self.trait_alias_to_def(container.with_value(it))?.into(),
+                ),
                 ast::Item::Struct(it) => {
                     let def = self.struct_to_def(container.with_value(it))?;
                     let is_in_body = it.field_list().is_some_and(|it| {
@@ -723,11 +738,9 @@ pub(crate) enum ChildContainer {
     DefWithBodyId(DefWithBodyId),
     ModuleId(ModuleId),
     TraitId(TraitId),
-    TraitAliasId(TraitAliasId),
     ImplId(ImplId),
     EnumId(EnumId),
     VariantId(VariantId),
-    TypeAliasId(TypeAliasId),
     /// XXX: this might be the same def as, for example an `EnumId`. However,
     /// here the children are generic parameters, and not, eg enum variants.
     GenericDefId(GenericDefId),
@@ -736,11 +749,9 @@ impl_from! {
     DefWithBodyId,
     ModuleId,
     TraitId,
-    TraitAliasId,
     ImplId,
     EnumId,
     VariantId,
-    TypeAliasId,
     GenericDefId
     for ChildContainer
 }
@@ -752,11 +763,9 @@ 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),
-            ChildContainer::TypeAliasId(_) => DynMap::default(),
             ChildContainer::GenericDefId(it) => it.child_by_source(db, file_id),
         }
     }
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs
index 5d75e445861..32c4ae2e869 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs
@@ -56,14 +56,22 @@ pub(crate) fn convert_named_struct_to_tuple_struct(
     // XXX: We don't currently provide this assist for struct definitions inside macros, but if we
     // are to lift this limitation, don't forget to make `edit_struct_def()` consider macro files
     // too.
-    let name = ctx.find_node_at_offset::<ast::Name>()?;
-    let strukt = name.syntax().parent().and_then(<Either<ast::Struct, ast::Variant>>::cast)?;
-    let field_list = strukt.as_ref().either(|s| s.field_list(), |v| v.field_list())?;
+    let strukt_or_variant = ctx
+        .find_node_at_offset::<ast::Struct>()
+        .map(Either::Left)
+        .or_else(|| ctx.find_node_at_offset::<ast::Variant>().map(Either::Right))?;
+    let field_list = strukt_or_variant.as_ref().either(|s| s.field_list(), |v| v.field_list())?;
+
+    if ctx.offset() > field_list.syntax().text_range().start() {
+        // Assist could be distracting after the braces
+        return None;
+    }
+
     let record_fields = match field_list {
         ast::FieldList::RecordFieldList(it) => it,
         ast::FieldList::TupleFieldList(_) => return None,
     };
-    let strukt_def = match &strukt {
+    let strukt_def = match &strukt_or_variant {
         Either::Left(s) => Either::Left(ctx.sema.to_def(s)?),
         Either::Right(v) => Either::Right(ctx.sema.to_def(v)?),
     };
@@ -71,11 +79,11 @@ pub(crate) fn convert_named_struct_to_tuple_struct(
     acc.add(
         AssistId::refactor_rewrite("convert_named_struct_to_tuple_struct"),
         "Convert to tuple struct",
-        strukt.syntax().text_range(),
+        strukt_or_variant.syntax().text_range(),
         |edit| {
             edit_field_references(ctx, edit, record_fields.fields());
             edit_struct_references(ctx, edit, strukt_def);
-            edit_struct_def(ctx, edit, &strukt, record_fields);
+            edit_struct_def(ctx, edit, &strukt_or_variant, record_fields);
         },
     )
 }
@@ -294,6 +302,88 @@ impl A {
     }
 
     #[test]
+    fn convert_simple_struct_cursor_on_struct_keyword() {
+        check_assist(
+            convert_named_struct_to_tuple_struct,
+            r#"
+struct Inner;
+struct$0 A { inner: Inner }
+
+impl A {
+    fn new(inner: Inner) -> A {
+        A { inner }
+    }
+
+    fn new_with_default() -> A {
+        A::new(Inner)
+    }
+
+    fn into_inner(self) -> Inner {
+        self.inner
+    }
+}"#,
+            r#"
+struct Inner;
+struct A(Inner);
+
+impl A {
+    fn new(inner: Inner) -> A {
+        A(inner)
+    }
+
+    fn new_with_default() -> A {
+        A::new(Inner)
+    }
+
+    fn into_inner(self) -> Inner {
+        self.0
+    }
+}"#,
+        );
+    }
+
+    #[test]
+    fn convert_simple_struct_cursor_on_visibility_keyword() {
+        check_assist(
+            convert_named_struct_to_tuple_struct,
+            r#"
+struct Inner;
+pub$0 struct A { inner: Inner }
+
+impl A {
+    fn new(inner: Inner) -> A {
+        A { inner }
+    }
+
+    fn new_with_default() -> A {
+        A::new(Inner)
+    }
+
+    fn into_inner(self) -> Inner {
+        self.inner
+    }
+}"#,
+            r#"
+struct Inner;
+pub struct A(Inner);
+
+impl A {
+    fn new(inner: Inner) -> A {
+        A(inner)
+    }
+
+    fn new_with_default() -> A {
+        A::new(Inner)
+    }
+
+    fn into_inner(self) -> Inner {
+        self.0
+    }
+}"#,
+        );
+    }
+
+    #[test]
     fn convert_struct_referenced_via_self_kw() {
         check_assist(
             convert_named_struct_to_tuple_struct,
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs
index 0c0b93bcfbc..80756197fb7 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs
@@ -51,18 +51,26 @@ pub(crate) fn convert_tuple_struct_to_named_struct(
     acc: &mut Assists,
     ctx: &AssistContext<'_>,
 ) -> Option<()> {
-    let name = ctx.find_node_at_offset::<ast::Name>()?;
-    let strukt = name.syntax().parent().and_then(<Either<ast::Struct, ast::Variant>>::cast)?;
-    let field_list = strukt.as_ref().either(|s| s.field_list(), |v| v.field_list())?;
+    let strukt_or_variant = ctx
+        .find_node_at_offset::<ast::Struct>()
+        .map(Either::Left)
+        .or_else(|| ctx.find_node_at_offset::<ast::Variant>().map(Either::Right))?;
+    let field_list = strukt_or_variant.as_ref().either(|s| s.field_list(), |v| v.field_list())?;
+
+    if ctx.offset() > field_list.syntax().text_range().start() {
+        // Assist could be distracting after the braces
+        return None;
+    }
+
     let tuple_fields = match field_list {
         ast::FieldList::TupleFieldList(it) => it,
         ast::FieldList::RecordFieldList(_) => return None,
     };
-    let strukt_def = match &strukt {
+    let strukt_def = match &strukt_or_variant {
         Either::Left(s) => Either::Left(ctx.sema.to_def(s)?),
         Either::Right(v) => Either::Right(ctx.sema.to_def(v)?),
     };
-    let target = strukt.as_ref().either(|s| s.syntax(), |v| v.syntax()).text_range();
+    let target = strukt_or_variant.as_ref().either(|s| s.syntax(), |v| v.syntax()).text_range();
 
     acc.add(
         AssistId::refactor_rewrite("convert_tuple_struct_to_named_struct"),
@@ -72,7 +80,7 @@ pub(crate) fn convert_tuple_struct_to_named_struct(
             let names = generate_names(tuple_fields.fields());
             edit_field_references(ctx, edit, tuple_fields.fields(), &names);
             edit_struct_references(ctx, edit, strukt_def, &names);
-            edit_struct_def(ctx, edit, &strukt, tuple_fields, names);
+            edit_struct_def(ctx, edit, &strukt_or_variant, tuple_fields, names);
         },
     )
 }
@@ -317,6 +325,88 @@ impl A {
     }
 
     #[test]
+    fn convert_simple_struct_cursor_on_struct_keyword() {
+        check_assist(
+            convert_tuple_struct_to_named_struct,
+            r#"
+struct Inner;
+struct$0 A(Inner);
+
+impl A {
+    fn new(inner: Inner) -> A {
+        A(inner)
+    }
+
+    fn new_with_default() -> A {
+        A::new(Inner)
+    }
+
+    fn into_inner(self) -> Inner {
+        self.0
+    }
+}"#,
+            r#"
+struct Inner;
+struct A { field1: Inner }
+
+impl A {
+    fn new(inner: Inner) -> A {
+        A { field1: inner }
+    }
+
+    fn new_with_default() -> A {
+        A::new(Inner)
+    }
+
+    fn into_inner(self) -> Inner {
+        self.field1
+    }
+}"#,
+        );
+    }
+
+    #[test]
+    fn convert_simple_struct_cursor_on_visibility_keyword() {
+        check_assist(
+            convert_tuple_struct_to_named_struct,
+            r#"
+struct Inner;
+pub$0 struct A(Inner);
+
+impl A {
+    fn new(inner: Inner) -> A {
+        A(inner)
+    }
+
+    fn new_with_default() -> A {
+        A::new(Inner)
+    }
+
+    fn into_inner(self) -> Inner {
+        self.0
+    }
+}"#,
+            r#"
+struct Inner;
+pub struct A { field1: Inner }
+
+impl A {
+    fn new(inner: Inner) -> A {
+        A { field1: inner }
+    }
+
+    fn new_with_default() -> A {
+        A::new(Inner)
+    }
+
+    fn into_inner(self) -> Inner {
+        self.field1
+    }
+}"#,
+        );
+    }
+
+    #[test]
     fn convert_struct_referenced_via_self_kw() {
         check_assist(
             convert_tuple_struct_to_named_struct,
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 19e0a73f333..3badc17d01a 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
@@ -7,10 +7,10 @@ use syntax::{
 
 use crate::{AssistContext, AssistId, Assists};
 
-// FIXME: this really should be a fix for diagnostic, rather than an assist.
-
 // Assist: fix_visibility
 //
+// Note that there is some duplication between this and the no_such_field diagnostic.
+//
 // Makes inaccessible item public.
 //
 // ```
@@ -32,7 +32,6 @@ use crate::{AssistContext, AssistId, Assists};
 // ```
 pub(crate) fn fix_visibility(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
     add_vis_to_referenced_module_def(acc, ctx)
-        .or_else(|| add_vis_to_referenced_record_field(acc, ctx))
 }
 
 fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
@@ -88,59 +87,6 @@ fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext<'_>)
     })
 }
 
-fn add_vis_to_referenced_record_field(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
-    let record_field: ast::RecordExprField = ctx.find_node_at_offset()?;
-    let (record_field_def, _, _) = ctx.sema.resolve_record_field(&record_field)?;
-
-    let current_module = ctx.sema.scope(record_field.syntax())?.module();
-    let current_edition = current_module.krate().edition(ctx.db());
-    let visibility = record_field_def.visibility(ctx.db());
-    if visibility.is_visible_from(ctx.db(), current_module.into()) {
-        return None;
-    }
-
-    let parent = record_field_def.parent_def(ctx.db());
-    let parent_name = parent.name(ctx.db());
-    let target_module = parent.module(ctx.db());
-
-    let in_file_source = record_field_def.source(ctx.db())?;
-    let (vis_owner, target) = match in_file_source.value {
-        hir::FieldSource::Named(it) => {
-            let range = it.syntax().text_range();
-            (ast::AnyHasVisibility::new(it), range)
-        }
-        hir::FieldSource::Pos(it) => {
-            let range = it.syntax().text_range();
-            (ast::AnyHasVisibility::new(it), range)
-        }
-    };
-
-    let missing_visibility = if current_module.krate() == target_module.krate() {
-        make::visibility_pub_crate()
-    } else {
-        make::visibility_pub()
-    };
-    let target_file = in_file_source.file_id.original_file(ctx.db());
-
-    let target_name = record_field_def.name(ctx.db());
-    let assist_label = format!(
-        "Change visibility of {}.{} to {missing_visibility}",
-        parent_name.display(ctx.db(), current_edition),
-        target_name.display(ctx.db(), current_edition)
-    );
-
-    acc.add(AssistId::quick_fix("fix_visibility"), assist_label, target, |edit| {
-        edit.edit_file(target_file.file_id(ctx.db()));
-
-        let vis_owner = edit.make_mut(vis_owner);
-        vis_owner.set_visibility(Some(missing_visibility.clone_for_update()));
-
-        if let Some((cap, vis)) = ctx.config.snippet_cap.zip(vis_owner.visibility()) {
-            edit.add_tabstop_before(cap, vis);
-        }
-    })
-}
-
 fn target_data_for_def(
     db: &dyn HirDatabase,
     def: hir::ModuleDef,
@@ -294,44 +240,6 @@ struct Foo;
     }
 
     #[test]
-    fn fix_visibility_of_struct_field() {
-        check_assist(
-            fix_visibility,
-            r"mod foo { pub struct Foo { bar: (), } }
-              fn main() { foo::Foo { $0bar: () }; } ",
-            r"mod foo { pub struct Foo { $0pub(crate) bar: (), } }
-              fn main() { foo::Foo { bar: () }; } ",
-        );
-        check_assist(
-            fix_visibility,
-            r"
-//- /lib.rs
-mod foo;
-fn main() { foo::Foo { $0bar: () }; }
-//- /foo.rs
-pub struct Foo { bar: () }
-",
-            r"pub struct Foo { $0pub(crate) bar: () }
-",
-        );
-        check_assist_not_applicable(
-            fix_visibility,
-            r"mod foo { pub struct Foo { pub bar: (), } }
-              fn main() { foo::Foo { $0bar: () }; } ",
-        );
-        check_assist_not_applicable(
-            fix_visibility,
-            r"
-//- /lib.rs
-mod foo;
-fn main() { foo::Foo { $0bar: () }; }
-//- /foo.rs
-pub struct Foo { pub bar: () }
-",
-        );
-    }
-
-    #[test]
     fn fix_visibility_of_enum_variant_field() {
         // Enum variants, as well as their fields, always get the enum's visibility. In fact, rustc
         // rejects any visibility specifiers on them, so this assist should never fire on them.
@@ -368,44 +276,6 @@ pub struct Foo { pub bar: () }
     }
 
     #[test]
-    fn fix_visibility_of_union_field() {
-        check_assist(
-            fix_visibility,
-            r"mod foo { pub union Foo { bar: (), } }
-              fn main() { foo::Foo { $0bar: () }; } ",
-            r"mod foo { pub union Foo { $0pub(crate) bar: (), } }
-              fn main() { foo::Foo { bar: () }; } ",
-        );
-        check_assist(
-            fix_visibility,
-            r"
-//- /lib.rs
-mod foo;
-fn main() { foo::Foo { $0bar: () }; }
-//- /foo.rs
-pub union Foo { bar: () }
-",
-            r"pub union Foo { $0pub(crate) bar: () }
-",
-        );
-        check_assist_not_applicable(
-            fix_visibility,
-            r"mod foo { pub union Foo { pub bar: (), } }
-              fn main() { foo::Foo { $0bar: () }; } ",
-        );
-        check_assist_not_applicable(
-            fix_visibility,
-            r"
-//- /lib.rs
-mod foo;
-fn main() { foo::Foo { $0bar: () }; }
-//- /foo.rs
-pub union Foo { pub bar: () }
-",
-        );
-    }
-
-    #[test]
     fn fix_visibility_of_const() {
         check_assist(
             fix_visibility,
@@ -572,19 +442,6 @@ pub(crate) struct Bar;
             r"$0pub struct Bar;
 ",
         );
-        check_assist(
-            fix_visibility,
-            r"
-//- /main.rs crate:a deps:foo
-fn main() {
-    foo::Foo { $0bar: () };
-}
-//- /lib.rs crate:foo
-pub struct Foo { pub(crate) bar: () }
-",
-            r"pub struct Foo { $0pub bar: () }
-",
-        );
     }
 
     #[test]
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_underscore.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_underscore.rs
index 912e1936b59..a8e27416d5c 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_underscore.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_underscore.rs
@@ -1,6 +1,7 @@
 use ide_db::{
     assists::AssistId,
     defs::{Definition, NameClass, NameRefClass},
+    rename::RenameDefinition,
 };
 use syntax::{AstNode, ast};
 
@@ -61,7 +62,7 @@ pub(crate) fn remove_underscore(acc: &mut Assists, ctx: &AssistContext<'_>) -> O
         "Remove underscore from a used variable",
         text_range,
         |builder| {
-            let changes = def.rename(&ctx.sema, new_name).unwrap();
+            let changes = def.rename(&ctx.sema, new_name, RenameDefinition::Yes).unwrap();
             builder.source_change = changes;
         },
     )
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs
index 3c195f80fea..705402c785a 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs
@@ -25,6 +25,7 @@ use crate::{
 
 mod cfg;
 mod derive;
+mod diagnostic;
 mod lint;
 mod macro_use;
 mod repr;
@@ -40,23 +41,22 @@ pub(crate) fn complete_known_attribute_input(
     extern_crate: Option<&ast::ExternCrate>,
 ) -> Option<()> {
     let attribute = fake_attribute_under_caret;
-    let name_ref = match attribute.path() {
-        Some(p) => Some(p.as_single_name_ref()?),
-        None => None,
-    };
-    let (path, tt) = name_ref.zip(attribute.token_tree())?;
-    tt.l_paren_token()?;
+    let path = attribute.path()?;
+    let segments = path.segments().map(|s| s.name_ref()).collect::<Option<Vec<_>>>()?;
+    let segments = segments.iter().map(|n| n.text()).collect::<Vec<_>>();
+    let segments = segments.iter().map(|t| t.as_str()).collect::<Vec<_>>();
+    let tt = attribute.token_tree()?;
 
-    match path.text().as_str() {
-        "repr" => repr::complete_repr(acc, ctx, tt),
-        "feature" => lint::complete_lint(
+    match segments.as_slice() {
+        ["repr"] => repr::complete_repr(acc, ctx, tt),
+        ["feature"] => lint::complete_lint(
             acc,
             ctx,
             colon_prefix,
             &parse_tt_as_comma_sep_paths(tt, ctx.edition)?,
             FEATURES,
         ),
-        "allow" | "expect" | "deny" | "forbid" | "warn" => {
+        ["allow"] | ["expect"] | ["deny"] | ["forbid"] | ["warn"] => {
             let existing_lints = parse_tt_as_comma_sep_paths(tt, ctx.edition)?;
 
             let lints: Vec<Lint> = CLIPPY_LINT_GROUPS
@@ -70,13 +70,14 @@ pub(crate) fn complete_known_attribute_input(
 
             lint::complete_lint(acc, ctx, colon_prefix, &existing_lints, &lints);
         }
-        "cfg" => cfg::complete_cfg(acc, ctx),
-        "macro_use" => macro_use::complete_macro_use(
+        ["cfg"] => cfg::complete_cfg(acc, ctx),
+        ["macro_use"] => macro_use::complete_macro_use(
             acc,
             ctx,
             extern_crate,
             &parse_tt_as_comma_sep_paths(tt, ctx.edition)?,
         ),
+        ["diagnostic", "on_unimplemented"] => diagnostic::complete_on_unimplemented(acc, ctx, tt),
         _ => (),
     }
     Some(())
@@ -139,6 +140,8 @@ pub(crate) fn complete_attribute_path(
         }
         Qualified::TypeAnchor { .. } | Qualified::With { .. } => {}
     }
+    let qualifier_path =
+        if let Qualified::With { path, .. } = qualified { Some(path) } else { None };
 
     let attributes = annotated_item_kind.and_then(|kind| {
         if ast::Expr::can_cast(kind) {
@@ -149,18 +152,33 @@ pub(crate) fn complete_attribute_path(
     });
 
     let add_completion = |attr_completion: &AttrCompletion| {
-        let mut item = CompletionItem::new(
-            SymbolKind::Attribute,
-            ctx.source_range(),
-            attr_completion.label,
-            ctx.edition,
-        );
+        // if we don't already have the qualifiers of the completion, then
+        // add the missing parts to the label and snippet
+        let mut label = attr_completion.label.to_owned();
+        let mut snippet = attr_completion.snippet.map(|s| s.to_owned());
+        let segments = qualifier_path.iter().flat_map(|q| q.segments()).collect::<Vec<_>>();
+        let qualifiers = attr_completion.qualifiers;
+        let matching_qualifiers = segments
+            .iter()
+            .zip(qualifiers)
+            .take_while(|(s, q)| s.name_ref().is_some_and(|t| t.text() == **q))
+            .count();
+        if matching_qualifiers != qualifiers.len() {
+            let prefix = qualifiers[matching_qualifiers..].join("::");
+            label = format!("{prefix}::{label}");
+            if let Some(s) = snippet.as_mut() {
+                *s = format!("{prefix}::{s}");
+            }
+        }
+
+        let mut item =
+            CompletionItem::new(SymbolKind::Attribute, ctx.source_range(), label, ctx.edition);
 
         if let Some(lookup) = attr_completion.lookup {
             item.lookup_by(lookup);
         }
 
-        if let Some((snippet, cap)) = attr_completion.snippet.zip(ctx.config.snippet_cap) {
+        if let Some((snippet, cap)) = snippet.zip(ctx.config.snippet_cap) {
             item.insert_snippet(cap, snippet);
         }
 
@@ -184,6 +202,7 @@ struct AttrCompletion {
     label: &'static str,
     lookup: Option<&'static str>,
     snippet: Option<&'static str>,
+    qualifiers: &'static [&'static str],
     prefer_inner: bool,
 }
 
@@ -192,6 +211,10 @@ impl AttrCompletion {
         self.lookup.unwrap_or(self.label)
     }
 
+    const fn qualifiers(self, qualifiers: &'static [&'static str]) -> AttrCompletion {
+        AttrCompletion { qualifiers, ..self }
+    }
+
     const fn prefer_inner(self) -> AttrCompletion {
         AttrCompletion { prefer_inner: true, ..self }
     }
@@ -202,7 +225,7 @@ const fn attr(
     lookup: Option<&'static str>,
     snippet: Option<&'static str>,
 ) -> AttrCompletion {
-    AttrCompletion { label, lookup, snippet, prefer_inner: false }
+    AttrCompletion { label, lookup, snippet, qualifiers: &[], prefer_inner: false }
 }
 
 macro_rules! attrs {
@@ -264,14 +287,14 @@ static KIND_TO_ATTRIBUTES: LazyLock<FxHashMap<SyntaxKind, &[&str]>> = LazyLock::
             FN,
             attrs!(
                 item, linkable,
-                "cold", "ignore", "inline", "must_use", "panic_handler", "proc_macro",
+                "cold", "ignore", "inline", "panic_handler", "proc_macro",
                 "proc_macro_derive", "proc_macro_attribute", "should_panic", "target_feature",
                 "test", "track_caller"
             ),
         ),
         (STATIC, attrs!(item, linkable, "global_allocator", "used")),
-        (TRAIT, attrs!(item, "must_use")),
-        (IMPL, attrs!(item, "automatically_derived")),
+        (TRAIT, attrs!(item, "diagnostic::on_unimplemented")),
+        (IMPL, attrs!(item, "automatically_derived", "diagnostic::do_not_recommend")),
         (ASSOC_ITEM_LIST, attrs!(item)),
         (EXTERN_BLOCK, attrs!(item, "link")),
         (EXTERN_ITEM_LIST, attrs!(item, "link")),
@@ -311,6 +334,14 @@ const ATTRIBUTES: &[AttrCompletion] = &[
     attr("deny(…)", Some("deny"), Some("deny(${0:lint})")),
     attr(r#"deprecated"#, Some("deprecated"), Some(r#"deprecated"#)),
     attr("derive(…)", Some("derive"), Some(r#"derive(${0:Debug})"#)),
+    attr("do_not_recommend", Some("diagnostic::do_not_recommend"), None)
+        .qualifiers(&["diagnostic"]),
+    attr(
+        "on_unimplemented",
+        Some("diagnostic::on_unimplemented"),
+        Some(r#"on_unimplemented(${0:keys})"#),
+    )
+    .qualifiers(&["diagnostic"]),
     attr(r#"doc = "…""#, Some("doc"), Some(r#"doc = "${0:docs}""#)),
     attr(r#"doc(alias = "…")"#, Some("docalias"), Some(r#"doc(alias = "${0:docs}")"#)),
     attr(r#"doc(hidden)"#, Some("dochidden"), Some(r#"doc(hidden)"#)),
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/diagnostic.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/diagnostic.rs
new file mode 100644
index 00000000000..8adc9742390
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/diagnostic.rs
@@ -0,0 +1,60 @@
+//! Completion for diagnostic attributes.
+
+use ide_db::SymbolKind;
+use syntax::ast;
+
+use crate::{CompletionItem, Completions, context::CompletionContext};
+
+use super::AttrCompletion;
+
+pub(super) fn complete_on_unimplemented(
+    acc: &mut Completions,
+    ctx: &CompletionContext<'_>,
+    input: ast::TokenTree,
+) {
+    if let Some(existing_keys) = super::parse_comma_sep_expr(input) {
+        for attr in ATTRIBUTE_ARGS {
+            let already_annotated = existing_keys
+                .iter()
+                .filter_map(|expr| match expr {
+                    ast::Expr::PathExpr(path) => path.path()?.as_single_name_ref(),
+                    ast::Expr::BinExpr(bin)
+                        if bin.op_kind() == Some(ast::BinaryOp::Assignment { op: None }) =>
+                    {
+                        match bin.lhs()? {
+                            ast::Expr::PathExpr(path) => path.path()?.as_single_name_ref(),
+                            _ => None,
+                        }
+                    }
+                    _ => None,
+                })
+                .any(|it| {
+                    let text = it.text();
+                    attr.key() == text && text != "note"
+                });
+            if already_annotated {
+                continue;
+            }
+
+            let mut item = CompletionItem::new(
+                SymbolKind::BuiltinAttr,
+                ctx.source_range(),
+                attr.label,
+                ctx.edition,
+            );
+            if let Some(lookup) = attr.lookup {
+                item.lookup_by(lookup);
+            }
+            if let Some((snippet, cap)) = attr.snippet.zip(ctx.config.snippet_cap) {
+                item.insert_snippet(cap, snippet);
+            }
+            item.add_to(acc, ctx.db);
+        }
+    }
+}
+
+const ATTRIBUTE_ARGS: &[AttrCompletion] = &[
+    super::attr(r#"label = "…""#, Some("label"), Some(r#"label = "${0:label}""#)),
+    super::attr(r#"message = "…""#, Some("message"), Some(r#"message = "${0:message}""#)),
+    super::attr(r#"note = "…""#, Some("note"), Some(r#"note = "${0:note}""#)),
+];
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 32d3b50f237..411902f1117 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
@@ -30,6 +30,8 @@ pub struct Foo(#[m$0] i32);
             at deprecated
             at derive                                  macro derive
             at derive(…)
+            at diagnostic::do_not_recommend
+            at diagnostic::on_unimplemented
             at doc = "…"
             at doc(alias = "…")
             at doc(hidden)
@@ -472,13 +474,13 @@ fn attr_on_trait() {
             at cfg_attr(…)
             at deny(…)
             at deprecated
+            at diagnostic::on_unimplemented
             at doc = "…"
             at doc(alias = "…")
             at doc(hidden)
             at expect(…)
             at forbid(…)
             at must_use
-            at must_use
             at no_mangle
             at warn(…)
             kw crate::
@@ -498,6 +500,7 @@ fn attr_on_impl() {
             at cfg_attr(…)
             at deny(…)
             at deprecated
+            at diagnostic::do_not_recommend
             at doc = "…"
             at doc(alias = "…")
             at doc(hidden)
@@ -533,6 +536,76 @@ fn attr_on_impl() {
 }
 
 #[test]
+fn attr_with_qualifier() {
+    check(
+        r#"#[diagnostic::$0] impl () {}"#,
+        expect![[r#"
+            at allow(…)
+            at automatically_derived
+            at cfg(…)
+            at cfg_attr(…)
+            at deny(…)
+            at deprecated
+            at do_not_recommend
+            at doc = "…"
+            at doc(alias = "…")
+            at doc(hidden)
+            at expect(…)
+            at forbid(…)
+            at must_use
+            at no_mangle
+            at warn(…)
+        "#]],
+    );
+    check(
+        r#"#[diagnostic::$0] trait Foo {}"#,
+        expect![[r#"
+            at allow(…)
+            at cfg(…)
+            at cfg_attr(…)
+            at deny(…)
+            at deprecated
+            at doc = "…"
+            at doc(alias = "…")
+            at doc(hidden)
+            at expect(…)
+            at forbid(…)
+            at must_use
+            at no_mangle
+            at on_unimplemented
+            at warn(…)
+        "#]],
+    );
+}
+
+#[test]
+fn attr_diagnostic_on_unimplemented() {
+    check(
+        r#"#[diagnostic::on_unimplemented($0)] trait Foo {}"#,
+        expect![[r#"
+            ba label = "…"
+            ba message = "…"
+            ba note = "…"
+        "#]],
+    );
+    check(
+        r#"#[diagnostic::on_unimplemented(message = "foo", $0)] trait Foo {}"#,
+        expect![[r#"
+            ba label = "…"
+            ba note = "…"
+        "#]],
+    );
+    check(
+        r#"#[diagnostic::on_unimplemented(note = "foo", $0)] trait Foo {}"#,
+        expect![[r#"
+            ba label = "…"
+            ba message = "…"
+            ba note = "…"
+        "#]],
+    );
+}
+
+#[test]
 fn attr_on_extern_block() {
     check(
         r#"#[$0] extern {}"#,
@@ -619,7 +692,6 @@ fn attr_on_fn() {
             at link_name = "…"
             at link_section = "…"
             at must_use
-            at must_use
             at no_mangle
             at panic_handler
             at proc_macro
@@ -649,6 +721,8 @@ fn attr_in_source_file_end() {
             at deny(…)
             at deprecated
             at derive(…)
+            at diagnostic::do_not_recommend
+            at diagnostic::on_unimplemented
             at doc = "…"
             at doc(alias = "…")
             at doc(hidden)
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 fa2a46a0f7c..4e737e27f05 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/rename.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/rename.rs
@@ -20,7 +20,7 @@
 //!
 //! The correct behavior in such cases is probably to show a dialog to the user.
 //! Our current behavior is ¯\_(ツ)_/¯.
-use std::fmt;
+use std::fmt::{self, Display};
 
 use crate::{
     source_change::ChangeAnnotation,
@@ -28,13 +28,12 @@ use crate::{
 };
 use base_db::AnchoredPathBuf;
 use either::Either;
-use hir::{EditionedFileId, FieldSource, FileRange, InFile, ModuleSource, Semantics};
+use hir::{FieldSource, FileRange, InFile, ModuleSource, Name, Semantics, sym};
 use span::{Edition, FileId, SyntaxContext};
 use stdx::{TupleExt, never};
 use syntax::{
     AstNode, SyntaxKind, T, TextRange,
     ast::{self, HasName},
-    utils::is_raw_identifier,
 };
 
 use crate::{
@@ -70,26 +69,33 @@ macro_rules! _bail {
 }
 pub use _bail as bail;
 
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub enum RenameDefinition {
+    Yes,
+    No,
+}
+
 impl Definition {
     pub fn rename(
         &self,
         sema: &Semantics<'_, RootDatabase>,
         new_name: &str,
+        rename_definition: RenameDefinition,
     ) -> Result<SourceChange> {
-        // We append `r#` if needed.
-        let new_name = new_name.trim_start_matches("r#");
-
         // self.krate() returns None if
         // self is a built-in attr, built-in type or tool module.
         // it is not allowed for these defs to be renamed.
         // cases where self.krate() is None is handled below.
-        if let Some(krate) = self.krate(sema.db) {
+        let edition = if let Some(krate) = self.krate(sema.db) {
             // Can we not rename non-local items?
             // Then bail if non-local
             if !krate.origin(sema.db).is_local() {
                 bail!("Cannot rename a non-local definition")
             }
-        }
+            krate.edition(sema.db)
+        } else {
+            Edition::LATEST
+        };
 
         match *self {
             Definition::Module(module) => rename_mod(sema, module, new_name),
@@ -103,8 +109,10 @@ impl Definition {
                 bail!("Cannot rename a builtin attr.")
             }
             Definition::SelfType(_) => bail!("Cannot rename `Self`"),
-            Definition::Macro(mac) => rename_reference(sema, Definition::Macro(mac), new_name),
-            def => rename_reference(sema, def, new_name),
+            Definition::Macro(mac) => {
+                rename_reference(sema, Definition::Macro(mac), new_name, rename_definition, edition)
+            }
+            def => rename_reference(sema, def, new_name, rename_definition, edition),
         }
     }
 
@@ -237,10 +245,6 @@ fn rename_mod(
     module: hir::Module,
     new_name: &str,
 ) -> Result<SourceChange> {
-    if IdentifierKind::classify(new_name)? != IdentifierKind::Ident {
-        bail!("Invalid name `{0}`: cannot rename module to {0}", new_name);
-    }
-
     let mut source_change = SourceChange::default();
 
     if module.is_crate_root() {
@@ -248,6 +252,14 @@ fn rename_mod(
     }
 
     let InFile { file_id, value: def_source } = module.definition_source(sema.db);
+    let edition = file_id.edition(sema.db);
+    let (new_name, kind) = IdentifierKind::classify(edition, new_name)?;
+    if kind != IdentifierKind::Ident {
+        bail!(
+            "Invalid name `{0}`: cannot rename module to {0}",
+            new_name.display(sema.db, edition)
+        );
+    }
     if let ModuleSource::SourceFile(..) = def_source {
         let anchor = file_id.original_file(sema.db).file_id(sema.db);
 
@@ -256,7 +268,7 @@ fn rename_mod(
 
         // Module exists in a named file
         if !is_mod_rs {
-            let path = format!("{new_name}.rs");
+            let path = format!("{}.rs", new_name.as_str());
             let dst = AnchoredPathBuf { anchor, path };
             source_change.push_file_system_edit(FileSystemEdit::MoveFile { src: anchor, dst })
         }
@@ -267,11 +279,11 @@ fn rename_mod(
         let dir_paths = match (is_mod_rs, has_detached_child, module.name(sema.db)) {
             // Go up one level since the anchor is inside the dir we're trying to rename
             (true, _, Some(mod_name)) => {
-                Some((format!("../{}", mod_name.as_str()), format!("../{new_name}")))
+                Some((format!("../{}", mod_name.as_str()), format!("../{}", new_name.as_str())))
             }
             // The anchor is on the same level as target dir
             (false, true, Some(mod_name)) => {
-                Some((mod_name.as_str().to_owned(), new_name.to_owned()))
+                Some((mod_name.as_str().to_owned(), new_name.as_str().to_owned()))
             }
             _ => None,
         };
@@ -296,11 +308,7 @@ fn rename_mod(
                     .original_file_range_opt(sema.db)
                     .map(TupleExt::head)
                 {
-                    let new_name = if is_raw_identifier(new_name, file_id.edition(sema.db)) {
-                        format!("r#{new_name}")
-                    } else {
-                        new_name.to_owned()
-                    };
+                    let new_name = new_name.display(sema.db, edition).to_string();
                     source_change.insert_source_edit(
                         file_id.file_id(sema.db),
                         TextEdit::replace(file_range.range, new_name),
@@ -314,9 +322,10 @@ fn rename_mod(
     let def = Definition::Module(module);
     let usages = def.usages(sema).all();
     let ref_edits = usages.iter().map(|(file_id, references)| {
+        let edition = file_id.edition(sema.db);
         (
             file_id.file_id(sema.db),
-            source_edit_from_references(references, def, new_name, file_id.edition(sema.db)),
+            source_edit_from_references(sema.db, references, def, &new_name, edition),
         )
     });
     source_change.extend(ref_edits);
@@ -328,8 +337,10 @@ fn rename_reference(
     sema: &Semantics<'_, RootDatabase>,
     def: Definition,
     new_name: &str,
+    rename_definition: RenameDefinition,
+    edition: Edition,
 ) -> Result<SourceChange> {
-    let ident_kind = IdentifierKind::classify(new_name)?;
+    let (mut new_name, ident_kind) = IdentifierKind::classify(edition, new_name)?;
 
     if matches!(
         def,
@@ -337,18 +348,34 @@ fn rename_reference(
     ) {
         match ident_kind {
             IdentifierKind::Underscore => {
-                bail!("Invalid name `{}`: not a lifetime identifier", new_name);
+                bail!(
+                    "Invalid name `{}`: not a lifetime identifier",
+                    new_name.display(sema.db, edition)
+                );
+            }
+            IdentifierKind::Ident => {
+                new_name = Name::new_lifetime(&format!("'{}", new_name.as_str()))
             }
-            _ => cov_mark::hit!(rename_lifetime),
+            IdentifierKind::Lifetime => (),
+            IdentifierKind::LowercaseSelf => bail!(
+                "Invalid name `{}`: not a lifetime identifier",
+                new_name.display(sema.db, edition)
+            ),
         }
     } else {
         match ident_kind {
             IdentifierKind::Lifetime => {
                 cov_mark::hit!(rename_not_an_ident_ref);
-                bail!("Invalid name `{}`: not an identifier", new_name);
+                bail!("Invalid name `{}`: not an identifier", new_name.display(sema.db, edition));
             }
             IdentifierKind::Ident => cov_mark::hit!(rename_non_local),
             IdentifierKind::Underscore => (),
+            IdentifierKind::LowercaseSelf => {
+                bail!(
+                    "Invalid name `{}`: cannot rename to `self`",
+                    new_name.display(sema.db, edition)
+                );
+            }
         }
     }
 
@@ -361,30 +388,29 @@ fn rename_reference(
     }
     let mut source_change = SourceChange::default();
     source_change.extend(usages.iter().map(|(file_id, references)| {
+        let edition = file_id.edition(sema.db);
         (
             file_id.file_id(sema.db),
-            source_edit_from_references(references, def, new_name, file_id.edition(sema.db)),
+            source_edit_from_references(sema.db, references, def, &new_name, edition),
         )
     }));
-
-    // This needs to come after the references edits, because we change the annotation of existing edits
-    // if a conflict is detected.
-    let (file_id, edit) = source_edit_from_def(sema, def, new_name, &mut source_change)?;
-    source_change.insert_source_edit(file_id, edit);
+    if rename_definition == RenameDefinition::Yes {
+        // This needs to come after the references edits, because we change the annotation of existing edits
+        // if a conflict is detected.
+        let (file_id, edit) = source_edit_from_def(sema, def, &new_name, &mut source_change)?;
+        source_change.insert_source_edit(file_id, edit);
+    }
     Ok(source_change)
 }
 
 pub fn source_edit_from_references(
+    db: &RootDatabase,
     references: &[FileReference],
     def: Definition,
-    new_name: &str,
+    new_name: &Name,
     edition: Edition,
 ) -> TextEdit {
-    let new_name = if is_raw_identifier(new_name, edition) {
-        format!("r#{new_name}")
-    } else {
-        new_name.to_owned()
-    };
+    let name_display = new_name.display(db, edition);
     let mut edit = TextEdit::builder();
     // macros can cause multiple refs to occur for the same text range, so keep track of what we have edited so far
     let mut edited_ranges = Vec::new();
@@ -395,23 +421,15 @@ pub fn source_edit_from_references(
             // to make special rewrites like shorthand syntax and such, so just rename the node in
             // the macro input
             FileReferenceNode::NameRef(name_ref) if name_range == range => {
-                source_edit_from_name_ref(&mut edit, name_ref, &new_name, def)
+                source_edit_from_name_ref(&mut edit, name_ref, &name_display, def)
             }
             FileReferenceNode::Name(name) if name_range == range => {
-                source_edit_from_name(&mut edit, name, &new_name)
+                source_edit_from_name(&mut edit, name, &name_display)
             }
             _ => false,
         };
         if !has_emitted_edit && !edited_ranges.contains(&range.start()) {
-            let (range, new_name) = match name {
-                FileReferenceNode::Lifetime(_) => (
-                    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);
+            edit.replace(range, name_display.to_string());
             edited_ranges.push(range.start());
         }
     }
@@ -419,7 +437,11 @@ pub fn source_edit_from_references(
     edit.finish()
 }
 
-fn source_edit_from_name(edit: &mut TextEditBuilder, name: &ast::Name, new_name: &str) -> bool {
+fn source_edit_from_name(
+    edit: &mut TextEditBuilder,
+    name: &ast::Name,
+    new_name: &dyn Display,
+) -> bool {
     if ast::RecordPatField::for_field_name(name).is_some() {
         if let Some(ident_pat) = name.syntax().parent().and_then(ast::IdentPat::cast) {
             cov_mark::hit!(rename_record_pat_field_name_split);
@@ -439,7 +461,7 @@ fn source_edit_from_name(edit: &mut TextEditBuilder, name: &ast::Name, new_name:
 fn source_edit_from_name_ref(
     edit: &mut TextEditBuilder,
     name_ref: &ast::NameRef,
-    new_name: &str,
+    new_name: &dyn Display,
     def: Definition,
 ) -> bool {
     if name_ref.super_token().is_some() {
@@ -452,6 +474,7 @@ fn source_edit_from_name_ref(
         match &(rcf_name_ref, rcf_expr.and_then(|it| expr_as_name_ref(&it))) {
             // field: init-expr, check if we can use a field init shorthand
             (Some(field_name), Some(init)) => {
+                let new_name = new_name.to_string();
                 if field_name == name_ref {
                     if init.text() == new_name {
                         cov_mark::hit!(test_rename_field_put_init_shorthand);
@@ -507,6 +530,7 @@ fn source_edit_from_name_ref(
             {
                 // field name is being renamed
                 if let Some(name) = pat.name() {
+                    let new_name = new_name.to_string();
                     if name.text() == new_name {
                         cov_mark::hit!(test_rename_field_put_init_shorthand_pat);
                         // Foo { field: ref mut local } -> Foo { ref mut field }
@@ -518,7 +542,7 @@ fn source_edit_from_name_ref(
                         let s = field_name.syntax().text_range().start();
                         let e = pat.syntax().text_range().start();
                         edit.delete(TextRange::new(s, e));
-                        edit.replace(name.syntax().text_range(), new_name.to_owned());
+                        edit.replace(name.syntax().text_range(), new_name);
                         return true;
                     }
                 }
@@ -532,16 +556,9 @@ fn source_edit_from_name_ref(
 fn source_edit_from_def(
     sema: &Semantics<'_, RootDatabase>,
     def: Definition,
-    new_name: &str,
+    new_name: &Name,
     source_change: &mut SourceChange,
 ) -> Result<(FileId, TextEdit)> {
-    let new_name_edition_aware = |new_name: &str, file_id: EditionedFileId| {
-        if is_raw_identifier(new_name, file_id.edition(sema.db)) {
-            format!("r#{new_name}")
-        } else {
-            new_name.to_owned()
-        }
-    };
     let mut edit = TextEdit::builder();
     if let Definition::Local(local) = def {
         let mut file_id = None;
@@ -573,7 +590,10 @@ fn source_edit_from_def(
                 {
                     Some(FileRange { file_id: file_id2, range }) => {
                         file_id = Some(file_id2);
-                        edit.replace(range, new_name_edition_aware(new_name, file_id2));
+                        edit.replace(
+                            range,
+                            new_name.display(sema.db, file_id2.edition(sema.db)).to_string(),
+                        );
                         continue;
                     }
                     None => {
@@ -587,7 +607,7 @@ fn source_edit_from_def(
                 // 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().as_str().trim_start_matches("r#")
+                        if new_name.as_str() == name_ref.text().as_str().trim_start_matches("r#")
                             && pat.at_token().is_none()
                         {
                             // Foo { field: ref mut local } -> Foo { ref mut field }
@@ -607,7 +627,9 @@ fn source_edit_from_def(
                             //                      ^^^^^ replace this with `new_name`
                             edit.replace(
                                 name_range,
-                                new_name_edition_aware(new_name, source.file_id),
+                                new_name
+                                    .display(sema.db, source.file_id.edition(sema.db))
+                                    .to_string(),
                             );
                         }
                     } else {
@@ -618,10 +640,16 @@ fn source_edit_from_def(
                             pat.syntax().text_range().start(),
                             format!("{}: ", pat_field.field_name().unwrap()),
                         );
-                        edit.replace(name_range, new_name_edition_aware(new_name, source.file_id));
+                        edit.replace(
+                            name_range,
+                            new_name.display(sema.db, source.file_id.edition(sema.db)).to_string(),
+                        );
                     }
                 } else {
-                    edit.replace(name_range, new_name_edition_aware(new_name, source.file_id));
+                    edit.replace(
+                        name_range,
+                        new_name.display(sema.db, source.file_id.edition(sema.db)).to_string(),
+                    );
                 }
             }
         }
@@ -639,16 +667,13 @@ fn source_edit_from_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(),
+        Definition::ExternCrateDecl(decl) if decl.alias(sema.db).is_none() => (
+            TextRange::empty(range.end()),
+            format!(" as {}", new_name.display(sema.db, file_id.edition(sema.db)),),
         ),
-        Definition::ExternCrateDecl(decl) if decl.alias(sema.db).is_none() => {
-            (TextRange::empty(range.end()), format!(" as {new_name}"))
-        }
-        _ => (range, new_name.to_owned()),
+        _ => (range, new_name.display(sema.db, file_id.edition(sema.db)).to_string()),
     };
-    edit.replace(range, new_name_edition_aware(&new_name, file_id));
+    edit.replace(range, new_name);
     Ok((file_id.file_id(sema.db), edit.finish()))
 }
 
@@ -657,26 +682,27 @@ pub enum IdentifierKind {
     Ident,
     Lifetime,
     Underscore,
+    LowercaseSelf,
 }
 
 impl IdentifierKind {
-    pub fn classify(new_name: &str) -> Result<IdentifierKind> {
-        let new_name = new_name.trim_start_matches("r#");
-        match parser::LexedStr::single_token(Edition::LATEST, new_name) {
+    pub fn classify(edition: Edition, new_name: &str) -> Result<(Name, IdentifierKind)> {
+        match parser::LexedStr::single_token(edition, new_name) {
             Some(res) => match res {
-                (SyntaxKind::IDENT, _) => {
-                    if let Some(inner) = new_name.strip_prefix("r#") {
-                        if matches!(inner, "self" | "crate" | "super" | "Self") {
-                            bail!("Invalid name: `{}` cannot be a raw identifier", inner);
-                        }
-                    }
-                    Ok(IdentifierKind::Ident)
+                (SyntaxKind::IDENT, _) => Ok((Name::new_root(new_name), IdentifierKind::Ident)),
+                (T![_], _) => {
+                    Ok((Name::new_symbol_root(sym::underscore), IdentifierKind::Underscore))
                 }
-                (T![_], _) => Ok(IdentifierKind::Underscore),
                 (SyntaxKind::LIFETIME_IDENT, _) if new_name != "'static" && new_name != "'_" => {
-                    Ok(IdentifierKind::Lifetime)
+                    Ok((Name::new_lifetime(new_name), IdentifierKind::Lifetime))
                 }
-                _ if is_raw_identifier(new_name, Edition::LATEST) => Ok(IdentifierKind::Ident),
+                _ if SyntaxKind::from_keyword(new_name, edition).is_some() => match new_name {
+                    "self" => Ok((Name::new_root(new_name), IdentifierKind::LowercaseSelf)),
+                    "crate" | "super" | "Self" => {
+                        bail!("Invalid name `{}`: cannot rename to a keyword", new_name)
+                    }
+                    _ => Ok((Name::new_root(new_name), IdentifierKind::Ident)),
+                },
                 (_, Some(syntax_error)) => bail!("Invalid name `{}`: {}", new_name, syntax_error),
                 (_, None) => bail!("Invalid name `{}`: not an identifier", new_name),
             },
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs
index 38f10c778d6..519ff192799 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs
@@ -1,5 +1,5 @@
 use hir::{CaseType, InFile, db::ExpandDatabase};
-use ide_db::{assists::Assist, defs::NameClass};
+use ide_db::{assists::Assist, defs::NameClass, rename::RenameDefinition};
 use syntax::AstNode;
 
 use crate::{
@@ -44,7 +44,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::IncorrectCase) -> Option<Vec<Ass
     let label = format!("Rename to {}", d.suggested_text);
     let mut res = unresolved_fix("change_case", &label, frange.range);
     if ctx.resolve.should_resolve(&res.id) {
-        let source_change = def.rename(&ctx.sema, &d.suggested_text);
+        let source_change = def.rename(&ctx.sema, &d.suggested_text, RenameDefinition::Yes);
         res.source_change = Some(source_change.ok().unwrap_or_default());
     }
 
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs
index 87c9397fb77..bf7dddacd8c 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs
@@ -135,6 +135,7 @@ pub(crate) fn json_in_items(
                         "JSON syntax is not valid as a Rust item",
                         FileRange { file_id: vfs_file_id, range },
                     )
+                    .stable()
                     .with_fixes(Some(vec![{
                         let mut scb = SourceChangeBuilder::new(vfs_file_id);
                         let scope = scb.make_import_scope_mut(import_scope);
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs
index 84fb467a5ce..ef42f2dc744 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs
@@ -1,4 +1,5 @@
 use either::Either;
+use hir::{Field, HasCrate};
 use hir::{HasSource, HirDisplay, Semantics, VariantId, db::ExpandDatabase};
 use ide_db::text_edit::TextEdit;
 use ide_db::{EditionedFileId, RootDatabase, source_change::SourceChange};
@@ -13,44 +14,69 @@ use crate::{Assist, Diagnostic, DiagnosticCode, DiagnosticsContext, fix};
 //
 // This diagnostic is triggered if created structure does not have field provided in record.
 pub(crate) fn no_such_field(ctx: &DiagnosticsContext<'_>, d: &hir::NoSuchField) -> Diagnostic {
-    let node = d.field.map(Into::into);
-    if d.private {
-        // FIXME: quickfix to add required visibility
-        Diagnostic::new_with_syntax_node_ptr(
-            ctx,
-            DiagnosticCode::RustcHardError("E0451"),
-            "field is private",
-            node,
-        )
-        .stable()
+    let (code, message) = if d.private.is_some() {
+        ("E0451", "field is private")
+    } else if let VariantId::EnumVariantId(_) = d.variant {
+        ("E0559", "no such field")
     } else {
-        Diagnostic::new_with_syntax_node_ptr(
-            ctx,
-            match d.variant {
-                VariantId::EnumVariantId(_) => DiagnosticCode::RustcHardError("E0559"),
-                _ => DiagnosticCode::RustcHardError("E0560"),
-            },
-            "no such field",
-            node,
-        )
+        ("E0560", "no such field")
+    };
+
+    let node = d.field.map(Into::into);
+    Diagnostic::new_with_syntax_node_ptr(ctx, DiagnosticCode::RustcHardError(code), message, node)
         .stable()
         .with_fixes(fixes(ctx, d))
-    }
 }
 
 fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::NoSuchField) -> Option<Vec<Assist>> {
     // FIXME: quickfix for pattern
     let root = ctx.sema.db.parse_or_expand(d.field.file_id);
     match &d.field.value.to_node(&root) {
-        Either::Left(node) => missing_record_expr_field_fixes(
-            &ctx.sema,
-            d.field.file_id.original_file(ctx.sema.db),
-            node,
-        ),
+        Either::Left(node) => {
+            if let Some(private_field) = d.private {
+                field_is_private_fixes(
+                    &ctx.sema,
+                    d.field.file_id.original_file(ctx.sema.db),
+                    node,
+                    private_field,
+                )
+            } else {
+                missing_record_expr_field_fixes(
+                    &ctx.sema,
+                    d.field.file_id.original_file(ctx.sema.db),
+                    node,
+                )
+            }
+        }
         _ => None,
     }
 }
 
+fn field_is_private_fixes(
+    sema: &Semantics<'_, RootDatabase>,
+    usage_file_id: EditionedFileId,
+    record_expr_field: &ast::RecordExprField,
+    private_field: Field,
+) -> Option<Vec<Assist>> {
+    let def_crate = private_field.krate(sema.db);
+    let usage_crate = sema.file_to_module_def(usage_file_id.file_id(sema.db))?.krate();
+    let visibility = if usage_crate == def_crate { "pub(crate) " } else { "pub " };
+
+    let source = private_field.source(sema.db)?;
+    let (range, _) = source.syntax().original_file_range_opt(sema.db)?;
+    let source_change = SourceChange::from_text_edit(
+        range.file_id.file_id(sema.db),
+        TextEdit::insert(range.range.start(), visibility.into()),
+    );
+
+    Some(vec![fix(
+        "increase_field_visibility",
+        "Increase field visibility",
+        source_change,
+        sema.original_range(record_expr_field.syntax()).range,
+    )])
+}
+
 fn missing_record_expr_field_fixes(
     sema: &Semantics<'_, RootDatabase>,
     usage_file_id: EditionedFileId,
@@ -118,7 +144,7 @@ fn missing_record_expr_field_fixes(
         "create_field",
         "Create field",
         source_change,
-        record_expr_field.syntax().text_range(),
+        sema.original_range(record_expr_field.syntax()).range,
     )]);
 
     fn record_field_list(field_def_list: ast::FieldList) -> Option<ast::RecordFieldList> {
@@ -387,15 +413,15 @@ fn f(s@m::Struct {
     // assignee expression
     m::Struct {
         field: 0,
-      //^^^^^^^^ error: field is private
+      //^^^^^^^^ 💡 error: field is private
         field2
-      //^^^^^^ error: field is private
+      //^^^^^^ 💡 error: field is private
     } = s;
     m::Struct {
         field: 0,
-      //^^^^^^^^ error: field is private
+      //^^^^^^^^ 💡 error: field is private
         field2
-      //^^^^^^ error: field is private
+      //^^^^^^ 💡 error: field is private
     };
 }
 "#,
@@ -403,6 +429,77 @@ fn f(s@m::Struct {
     }
 
     #[test]
+    fn test_struct_field_private_same_crate_fix() {
+        check_diagnostics(
+            r#"
+mod m {
+    pub struct Struct {
+        field: u32,
+    }
+}
+fn f() {
+    let _ = m::Struct {
+        field: 0,
+      //^^^^^^^^ 💡 error: field is private
+    };
+}
+"#,
+        );
+
+        check_fix(
+            r#"
+mod m {
+    pub struct Struct {
+        field: u32,
+    }
+}
+fn f() {
+    let _ = m::Struct {
+        field$0: 0,
+    };
+}
+"#,
+            r#"
+mod m {
+    pub struct Struct {
+        pub(crate) field: u32,
+    }
+}
+fn f() {
+    let _ = m::Struct {
+        field: 0,
+    };
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn test_struct_field_private_other_crate_fix() {
+        check_fix(
+            r#"
+//- /lib.rs crate:another_crate
+pub struct Struct {
+    field: u32,
+}
+//- /lib.rs crate:this_crate deps:another_crate
+use another_crate;
+
+fn f() {
+    let _ = another_crate::Struct {
+        field$0: 0,
+    };
+}
+"#,
+            r#"
+pub struct Struct {
+    pub field: u32,
+}
+"#,
+        );
+    }
+
+    #[test]
     fn editions_between_macros() {
         check_diagnostics(
             r#"
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs
index af9126c8933..d96c658d7b0 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs
@@ -69,6 +69,7 @@ pub(crate) fn unlinked_file(
             FileRange { file_id, range },
         )
         .with_unused(unused)
+        .stable()
         .with_fixes(fixes),
     );
 }
diff --git a/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs b/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs
index 7c396339c14..f31886b9697 100644
--- a/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs
@@ -800,4 +800,65 @@ foo();
                 foo();"#]],
         );
     }
+
+    #[test]
+    fn works_in_sig() {
+        check(
+            r#"
+macro_rules! foo {
+    () => { u32 };
+}
+fn foo() -> foo$0!() {
+    42
+}
+"#,
+            expect![[r#"
+                foo!
+                u32"#]],
+        );
+        check(
+            r#"
+macro_rules! foo {
+    () => { u32 };
+}
+fn foo(_: foo$0!() ) {}
+"#,
+            expect![[r#"
+                foo!
+                u32"#]],
+        );
+    }
+
+    #[test]
+    fn works_in_generics() {
+        check(
+            r#"
+trait Trait {}
+macro_rules! foo {
+    () => { Trait };
+}
+impl<const C: foo$0!()> Trait for () {}
+"#,
+            expect![[r#"
+                foo!
+                Trait"#]],
+        );
+    }
+
+    #[test]
+    fn works_in_fields() {
+        check(
+            r#"
+macro_rules! foo {
+    () => { u32 };
+}
+struct S {
+    field: foo$0!(),
+}
+"#,
+            expect![[r#"
+                foo!
+                u32"#]],
+        );
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs
index 82704af647d..b094b098462 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs
@@ -6,7 +6,7 @@ use std::{
 use either::Either;
 use hir::{
     ClosureStyle, DisplayTarget, EditionedFileId, HasVisibility, HirDisplay, HirDisplayError,
-    HirWrite, ModuleDef, ModuleDefId, Semantics, sym,
+    HirWrite, InRealFile, ModuleDef, ModuleDefId, Semantics, sym,
 };
 use ide_db::{FileRange, RootDatabase, famous_defs::FamousDefs, text_edit::TextEditBuilder};
 use ide_db::{FxHashSet, text_edit::TextEdit};
@@ -34,6 +34,7 @@ mod extern_block;
 mod generic_param;
 mod implicit_drop;
 mod implicit_static;
+mod implied_dyn_trait;
 mod lifetime;
 mod param_name;
 mod range_exclusive;
@@ -95,16 +96,16 @@ pub(crate) fn inlay_hints(
         return acc;
     };
     let famous_defs = FamousDefs(&sema, scope.krate());
+    let display_target = famous_defs.1.to_display_target(sema.db);
 
     let ctx = &mut InlayHintCtx::default();
     let mut hints = |event| {
         if let Some(node) = handle_event(ctx, event) {
-            hints(&mut acc, ctx, &famous_defs, config, file_id, node);
+            hints(&mut acc, ctx, &famous_defs, config, file_id, display_target, node);
         }
     };
     let mut preorder = file.preorder();
     while let Some(event) = preorder.next() {
-        // FIXME: This can miss some hints that require the parent of the range to calculate
         if matches!((&event, range_limit), (WalkEvent::Enter(node), Some(range)) if range.intersect(node.text_range()).is_none())
         {
             preorder.skip_subtree();
@@ -144,10 +145,12 @@ pub(crate) fn inlay_hints_resolve(
     let famous_defs = FamousDefs(&sema, scope.krate());
     let mut acc = Vec::new();
 
+    let display_target = famous_defs.1.to_display_target(sema.db);
+
     let ctx = &mut InlayHintCtx::default();
     let mut hints = |event| {
         if let Some(node) = handle_event(ctx, event) {
-            hints(&mut acc, ctx, &famous_defs, config, file_id, node);
+            hints(&mut acc, ctx, &famous_defs, config, file_id, display_target, node);
         }
     };
 
@@ -202,17 +205,19 @@ fn handle_event(ctx: &mut InlayHintCtx, node: WalkEvent<SyntaxNode>) -> Option<S
 fn hints(
     hints: &mut Vec<InlayHint>,
     ctx: &mut InlayHintCtx,
-    famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>,
+    famous_defs @ FamousDefs(sema, _krate): &FamousDefs<'_, '_>,
     config: &InlayHintsConfig,
     file_id: EditionedFileId,
+    display_target: DisplayTarget,
     node: SyntaxNode,
 ) {
-    let file_id = file_id.editioned_file_id(sema.db);
-    let Some(krate) = sema.first_crate(file_id.file_id()) else {
-        return;
-    };
-    let display_target = krate.to_display_target(sema.db);
-    closing_brace::hints(hints, sema, config, file_id, display_target, node.clone());
+    closing_brace::hints(
+        hints,
+        sema,
+        config,
+        display_target,
+        InRealFile { file_id, value: node.clone() },
+    );
     if let Some(any_has_generic_args) = ast::AnyHasGenericArgs::cast(node.clone()) {
         generic_param::hints(hints, famous_defs, config, any_has_generic_args);
     }
@@ -231,18 +236,18 @@ fn hints(
                         closure_captures::hints(hints, famous_defs, config, it.clone());
                         closure_ret::hints(hints, famous_defs, config, display_target, it)
                     },
-                    ast::Expr::RangeExpr(it) => range_exclusive::hints(hints, famous_defs, config, file_id,  it),
+                    ast::Expr::RangeExpr(it) => range_exclusive::hints(hints, famous_defs, config, it),
                     _ => Some(()),
                 }
             },
             ast::Pat(it) => {
-                binding_mode::hints(hints, famous_defs, config, file_id,  &it);
+                binding_mode::hints(hints, famous_defs, config, &it);
                 match it {
                     ast::Pat::IdentPat(it) => {
                         bind_pat::hints(hints, famous_defs, config, display_target, &it);
                     }
                     ast::Pat::RangePat(it) => {
-                        range_exclusive::hints(hints, famous_defs, config, file_id, it);
+                        range_exclusive::hints(hints, famous_defs, config, it);
                     }
                     _ => {}
                 }
@@ -250,30 +255,38 @@ fn hints(
             },
             ast::Item(it) => match it {
                 ast::Item::Fn(it) => {
-                    implicit_drop::hints(hints, famous_defs, config, file_id, &it);
+                    implicit_drop::hints(hints, famous_defs, config, display_target, &it);
                     if let Some(extern_block) = &ctx.extern_block_parent {
-                        extern_block::fn_hints(hints, famous_defs, config, file_id, &it, extern_block);
+                        extern_block::fn_hints(hints, famous_defs, config, &it, extern_block);
                     }
-                    lifetime::fn_hints(hints, ctx, famous_defs, config, file_id, it)
+                    lifetime::fn_hints(hints, ctx, famous_defs, config,  it)
                 },
                 ast::Item::Static(it) => {
                     if let Some(extern_block) = &ctx.extern_block_parent {
-                        extern_block::static_hints(hints, famous_defs, config, file_id, &it, extern_block);
+                        extern_block::static_hints(hints, famous_defs, config, &it, extern_block);
                     }
-                    implicit_static::hints(hints, famous_defs, config, file_id, Either::Left(it))
+                    implicit_static::hints(hints, famous_defs, config,  Either::Left(it))
                 },
-                ast::Item::Const(it) => implicit_static::hints(hints, famous_defs, config, file_id, Either::Right(it)),
-                ast::Item::Enum(it) => discriminant::enum_hints(hints, famous_defs, config, file_id, it),
-                ast::Item::ExternBlock(it) => extern_block::extern_block_hints(hints, famous_defs, config, file_id, it),
+                ast::Item::Const(it) => implicit_static::hints(hints, famous_defs, config, Either::Right(it)),
+                ast::Item::Enum(it) => discriminant::enum_hints(hints, famous_defs, config, it),
+                ast::Item::ExternBlock(it) => extern_block::extern_block_hints(hints, famous_defs, config, it),
                 _ => None,
             },
             // FIXME: trait object type elisions
             ast::Type(ty) => match ty {
-                ast::Type::FnPtrType(ptr) => lifetime::fn_ptr_hints(hints, ctx, famous_defs, config, file_id, ptr),
-                ast::Type::PathType(path) => lifetime::fn_path_hints(hints, ctx, famous_defs, config, file_id, path),
+                ast::Type::FnPtrType(ptr) => lifetime::fn_ptr_hints(hints, ctx, famous_defs, config,  ptr),
+                ast::Type::PathType(path) => {
+                    lifetime::fn_path_hints(hints, ctx, famous_defs, config, &path);
+                    implied_dyn_trait::hints(hints, famous_defs, config, Either::Left(path));
+                    Some(())
+                },
+                ast::Type::DynTraitType(dyn_) => {
+                    implied_dyn_trait::hints(hints, famous_defs, config, Either::Right(dyn_));
+                    Some(())
+                },
                 _ => Some(()),
             },
-            ast::GenericParamList(it) => bounds::hints(hints, famous_defs, config, file_id, it),
+            ast::GenericParamList(it) => bounds::hints(hints, famous_defs, config,  it),
             _ => Some(()),
         }
     };
@@ -438,6 +451,7 @@ pub enum InlayKind {
     Parameter,
     GenericParameter,
     Type,
+    Dyn,
     Drop,
     RangeExclusive,
     ExternUnsafety,
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs
index d2917320688..169ab92342b 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs
@@ -8,7 +8,6 @@ use hir::Mutability;
 use ide_db::famous_defs::FamousDefs;
 
 use ide_db::text_edit::TextEditBuilder;
-use span::EditionedFileId;
 use syntax::ast::{self, AstNode};
 
 use crate::{InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, InlayKind};
@@ -17,7 +16,6 @@ pub(super) fn hints(
     acc: &mut Vec<InlayHint>,
     FamousDefs(sema, _): &FamousDefs<'_, '_>,
     config: &InlayHintsConfig,
-    _file_id: EditionedFileId,
     pat: &ast::Pat,
 ) -> Option<()> {
     if !config.binding_mode_hints {
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bounds.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bounds.rs
index 8ddbfaeffe8..b9a98f88be7 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bounds.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bounds.rs
@@ -3,7 +3,6 @@
 //! Currently this renders the implied `Sized` bound.
 use ide_db::{FileRange, famous_defs::FamousDefs};
 
-use span::EditionedFileId;
 use syntax::ast::{self, AstNode, HasTypeBounds};
 
 use crate::{
@@ -15,7 +14,6 @@ pub(super) fn hints(
     acc: &mut Vec<InlayHint>,
     famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>,
     config: &InlayHintsConfig,
-    _file_id: EditionedFileId,
     params: ast::GenericParamList,
 ) -> Option<()> {
     if !config.sized_bound {
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs
index 2ec85da4a42..ca3a982760f 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs
@@ -3,9 +3,8 @@
 //! fn g() {
 //! } /* fn g */
 //! ```
-use hir::{DisplayTarget, HirDisplay, Semantics};
+use hir::{DisplayTarget, HirDisplay, InRealFile, Semantics};
 use ide_db::{FileRange, RootDatabase};
-use span::EditionedFileId;
 use syntax::{
     SyntaxKind, SyntaxNode, T,
     ast::{self, AstNode, HasLoopBody, HasName},
@@ -21,15 +20,14 @@ pub(super) fn hints(
     acc: &mut Vec<InlayHint>,
     sema: &Semantics<'_, RootDatabase>,
     config: &InlayHintsConfig,
-    file_id: EditionedFileId,
     display_target: DisplayTarget,
-    original_node: SyntaxNode,
+    InRealFile { file_id, value: node }: InRealFile<SyntaxNode>,
 ) -> Option<()> {
     let min_lines = config.closing_brace_hints_min_lines?;
 
     let name = |it: ast::Name| it.syntax().text_range();
 
-    let mut node = original_node.clone();
+    let mut node = node.clone();
     let mut closing_token;
     let (label, name_range) = if let Some(item_list) = ast::AssocItemList::cast(node.clone()) {
         closing_token = item_list.r_curly_token()?;
@@ -44,7 +42,7 @@ pub(super) fn hints(
                     let hint_text = match trait_ {
                         Some(tr) => format!(
                             "impl {} for {}",
-                            tr.name(sema.db).display(sema.db, file_id.edition()),
+                            tr.name(sema.db).display(sema.db, display_target.edition),
                             ty.display_truncated(sema.db, config.max_length, display_target,
                         )),
                         None => format!("impl {}", ty.display_truncated(sema.db, config.max_length, display_target)),
@@ -142,7 +140,8 @@ pub(super) fn hints(
         return None;
     }
 
-    let linked_location = name_range.map(|range| FileRange { file_id: file_id.into(), range });
+    let linked_location =
+        name_range.map(|range| FileRange { file_id: file_id.file_id(sema.db), range });
     acc.push(InlayHint {
         range: closing_token.text_range(),
         kind: InlayKind::ClosingBrace,
@@ -151,7 +150,7 @@ pub(super) fn hints(
         position: InlayHintPosition::After,
         pad_left: true,
         pad_right: false,
-        resolve_parent: Some(original_node.text_range()),
+        resolve_parent: Some(node.text_range()),
     });
 
     None
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 827a0438dd0..a2a702835a7 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
@@ -7,7 +7,6 @@
 use hir::Semantics;
 use ide_db::text_edit::TextEdit;
 use ide_db::{RootDatabase, famous_defs::FamousDefs};
-use span::EditionedFileId;
 use syntax::ast::{self, AstNode, HasName};
 
 use crate::{
@@ -19,7 +18,6 @@ pub(super) fn enum_hints(
     acc: &mut Vec<InlayHint>,
     FamousDefs(sema, _): &FamousDefs<'_, '_>,
     config: &InlayHintsConfig,
-    _: EditionedFileId,
     enum_: ast::Enum,
 ) -> Option<()> {
     if let DiscriminantHints::Never = config.discriminant_hints {
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/extern_block.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/extern_block.rs
index 20f54b2cd19..88152bf3e38 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/extern_block.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/extern_block.rs
@@ -1,6 +1,5 @@
 //! Extern block hints
 use ide_db::{famous_defs::FamousDefs, text_edit::TextEdit};
-use span::EditionedFileId;
 use syntax::{AstNode, SyntaxToken, ast};
 
 use crate::{InlayHint, InlayHintsConfig};
@@ -9,7 +8,6 @@ pub(super) fn extern_block_hints(
     acc: &mut Vec<InlayHint>,
     FamousDefs(sema, _): &FamousDefs<'_, '_>,
     config: &InlayHintsConfig,
-    _file_id: EditionedFileId,
     extern_block: ast::ExternBlock,
 ) -> Option<()> {
     if extern_block.unsafe_token().is_some() {
@@ -36,7 +34,6 @@ pub(super) fn fn_hints(
     acc: &mut Vec<InlayHint>,
     FamousDefs(sema, _): &FamousDefs<'_, '_>,
     config: &InlayHintsConfig,
-    _file_id: EditionedFileId,
     fn_: &ast::Fn,
     extern_block: &ast::ExternBlock,
 ) -> Option<()> {
@@ -55,7 +52,6 @@ pub(super) fn static_hints(
     acc: &mut Vec<InlayHint>,
     FamousDefs(sema, _): &FamousDefs<'_, '_>,
     config: &InlayHintsConfig,
-    _file_id: EditionedFileId,
     static_: &ast::Static,
     extern_block: &ast::ExternBlock,
 ) -> Option<()> {
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs
index f52e27946ff..bf4688e9d82 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs
@@ -12,7 +12,6 @@ use hir::{
 };
 use ide_db::{FileRange, famous_defs::FamousDefs};
 
-use span::EditionedFileId;
 use syntax::{
     ToSmolStr,
     ast::{self, AstNode},
@@ -25,7 +24,7 @@ pub(super) fn hints(
     acc: &mut Vec<InlayHint>,
     FamousDefs(sema, _): &FamousDefs<'_, '_>,
     config: &InlayHintsConfig,
-    file_id: EditionedFileId,
+    display_target: hir::DisplayTarget,
     node: &ast::Fn,
 ) -> Option<()> {
     if !config.implicit_drop_hints {
@@ -94,7 +93,7 @@ pub(super) fn hints(
                 MirSpan::Unknown => continue,
             };
             let binding = &hir.bindings[binding_idx];
-            let name = binding.name.display_no_db(file_id.edition()).to_smolstr();
+            let name = binding.name.display_no_db(display_target.edition).to_smolstr();
             if name.starts_with("<ra@") {
                 continue; // Ignore desugared variables
             }
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_static.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_static.rs
index f3be09f30a1..7212efd954e 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_static.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_static.rs
@@ -5,7 +5,6 @@
 use either::Either;
 use ide_db::famous_defs::FamousDefs;
 use ide_db::text_edit::TextEdit;
-use span::EditionedFileId;
 use syntax::{
     SyntaxKind,
     ast::{self, AstNode},
@@ -17,7 +16,6 @@ pub(super) fn hints(
     acc: &mut Vec<InlayHint>,
     FamousDefs(_sema, _): &FamousDefs<'_, '_>,
     config: &InlayHintsConfig,
-    _file_id: EditionedFileId,
     statik_or_const: Either<ast::Static, ast::Const>,
 ) -> Option<()> {
     if config.lifetime_elision_hints != LifetimeElisionHints::Always {
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implied_dyn_trait.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implied_dyn_trait.rs
new file mode 100644
index 00000000000..32d130503a4
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implied_dyn_trait.rs
@@ -0,0 +1,133 @@
+//! Implementation of trait bound hints.
+//!
+//! Currently this renders the implied `Sized` bound.
+use either::Either;
+use ide_db::{famous_defs::FamousDefs, text_edit::TextEdit};
+
+use syntax::ast::{self, AstNode};
+
+use crate::{InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, InlayKind};
+
+pub(super) fn hints(
+    acc: &mut Vec<InlayHint>,
+    FamousDefs(sema, _): &FamousDefs<'_, '_>,
+    config: &InlayHintsConfig,
+    path: Either<ast::PathType, ast::DynTraitType>,
+) -> Option<()> {
+    let parent = path.syntax().parent()?;
+    let range = match path {
+        Either::Left(path) => {
+            let paren =
+                parent.ancestors().take_while(|it| ast::ParenType::can_cast(it.kind())).last();
+            let parent = paren.as_ref().and_then(|it| it.parent()).unwrap_or(parent);
+            if ast::TypeBound::can_cast(parent.kind())
+                || ast::TypeAnchor::can_cast(parent.kind())
+                || ast::Impl::cast(parent)
+                    .and_then(|it| it.trait_())
+                    .is_some_and(|it| it.syntax() == path.syntax())
+            {
+                return None;
+            }
+            sema.resolve_trait(&path.path()?)?;
+            paren.map_or_else(|| path.syntax().text_range(), |it| it.text_range())
+        }
+        Either::Right(dyn_) => {
+            if dyn_.dyn_token().is_some() {
+                return None;
+            }
+
+            dyn_.syntax().text_range()
+        }
+    };
+
+    acc.push(InlayHint {
+        range,
+        kind: InlayKind::Dyn,
+        label: InlayHintLabel::simple("dyn", None, None),
+        text_edit: Some(
+            config.lazy_text_edit(|| TextEdit::insert(range.start(), "dyn ".to_owned())),
+        ),
+        position: InlayHintPosition::Before,
+        pad_left: false,
+        pad_right: true,
+        resolve_parent: Some(range),
+    });
+
+    Some(())
+}
+
+#[cfg(test)]
+mod tests {
+
+    use expect_test::expect;
+
+    use crate::inlay_hints::InlayHintsConfig;
+
+    use crate::inlay_hints::tests::{DISABLED_CONFIG, check_edit, check_with_config};
+
+    #[track_caller]
+    fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
+        check_with_config(InlayHintsConfig { sized_bound: true, ..DISABLED_CONFIG }, ra_fixture);
+    }
+
+    #[test]
+    fn path_works() {
+        check(
+            r#"
+struct S {}
+trait T {}
+fn foo(_: T,  _: dyn T, _: S) {}
+       // ^ dyn
+fn foo(_: &T,  _: for<'a> T) {}
+        // ^ dyn
+                       // ^ dyn
+impl T {}
+  // ^ dyn
+impl T for (T) {}
+        // ^^^ dyn
+"#,
+        );
+    }
+
+    #[test]
+    fn missing_dyn_bounds() {
+        check(
+            r#"
+trait T {}
+fn foo(
+    _: T + T,
+    // ^^^^^ dyn
+    _: T + 'a,
+    // ^^^^^^ dyn
+    _: 'a + T,
+    // ^^^^^^ dyn
+    _: &(T + T)
+    //   ^^^^^ dyn
+    _: &mut (T + T)
+    //       ^^^^^ dyn
+    _: *mut (T),
+    //      ^^^ dyn
+) {}
+"#,
+        );
+    }
+
+    #[test]
+    fn edit() {
+        check_edit(
+            DISABLED_CONFIG,
+            r#"
+trait T {}
+fn foo(
+    _: &mut T
+) {}
+"#,
+            expect![[r#"
+                trait T {}
+                fn foo(
+                    _: &mut dyn T
+                ) {}
+            "#]],
+        );
+    }
+}
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/lifetime.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/lifetime.rs
index baba49a427d..0069452e7b9 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/lifetime.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/lifetime.rs
@@ -6,7 +6,6 @@ use std::iter;
 
 use ide_db::{FxHashMap, famous_defs::FamousDefs, syntax_helpers::node_ext::walk_ty};
 use itertools::Itertools;
-use span::EditionedFileId;
 use syntax::{SmolStr, format_smolstr};
 use syntax::{
     SyntaxKind, SyntaxToken,
@@ -23,7 +22,6 @@ pub(super) fn fn_hints(
     ctx: &mut InlayHintCtx,
     fd: &FamousDefs<'_, '_>,
     config: &InlayHintsConfig,
-    file_id: EditionedFileId,
     func: ast::Fn,
 ) -> Option<()> {
     if config.lifetime_elision_hints == LifetimeElisionHints::Never {
@@ -40,7 +38,6 @@ pub(super) fn fn_hints(
         ctx,
         fd,
         config,
-        file_id,
         param_list.params().filter_map(|it| {
             Some((
                 it.pat().and_then(|it| match it {
@@ -74,7 +71,6 @@ pub(super) fn fn_ptr_hints(
     ctx: &mut InlayHintCtx,
     fd: &FamousDefs<'_, '_>,
     config: &InlayHintsConfig,
-    file_id: EditionedFileId,
     func: ast::FnPtrType,
 ) -> Option<()> {
     if config.lifetime_elision_hints == LifetimeElisionHints::Never {
@@ -97,7 +93,6 @@ pub(super) fn fn_ptr_hints(
         ctx,
         fd,
         config,
-        file_id,
         param_list.params().filter_map(|it| {
             Some((
                 it.pat().and_then(|it| match it {
@@ -140,8 +135,7 @@ pub(super) fn fn_path_hints(
     ctx: &mut InlayHintCtx,
     fd: &FamousDefs<'_, '_>,
     config: &InlayHintsConfig,
-    file_id: EditionedFileId,
-    func: ast::PathType,
+    func: &ast::PathType,
 ) -> Option<()> {
     if config.lifetime_elision_hints == LifetimeElisionHints::Never {
         return None;
@@ -163,7 +157,6 @@ pub(super) fn fn_path_hints(
         ctx,
         fd,
         config,
-        file_id,
         param_list.type_args().filter_map(|it| Some((None, it.ty()?))),
         generic_param_list,
         ret_type,
@@ -202,7 +195,6 @@ fn hints_(
     ctx: &mut InlayHintCtx,
     FamousDefs(_, _): &FamousDefs<'_, '_>,
     config: &InlayHintsConfig,
-    _file_id: EditionedFileId,
     params: impl Iterator<Item = (Option<ast::Name>, ast::Type)>,
     generic_param_list: Option<ast::GenericParamList>,
     ret_type: Option<ast::RetType>,
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/range_exclusive.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/range_exclusive.rs
index d67d8458840..47bd6d737f8 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/range_exclusive.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/range_exclusive.rs
@@ -4,7 +4,6 @@
 //! if let ../* < */100 = 50 {}
 //! ```
 use ide_db::famous_defs::FamousDefs;
-use span::EditionedFileId;
 use syntax::{SyntaxToken, T, ast};
 
 use crate::{InlayHint, InlayHintsConfig};
@@ -13,7 +12,6 @@ pub(super) fn hints(
     acc: &mut Vec<InlayHint>,
     FamousDefs(_sema, _): &FamousDefs<'_, '_>,
     config: &InlayHintsConfig,
-    _file_id: EditionedFileId,
     range: impl ast::RangeItem,
 ) -> Option<()> {
     (config.range_exclusive_hints && range.end().is_some())
diff --git a/src/tools/rust-analyzer/crates/ide/src/rename.rs b/src/tools/rust-analyzer/crates/ide/src/rename.rs
index 0423e3da2c8..fb84e8e6b47 100644
--- a/src/tools/rust-analyzer/crates/ide/src/rename.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/rename.rs
@@ -4,11 +4,11 @@
 //! tests. This module also implements a couple of magic tricks, like renaming
 //! `self` and to `self` (to switch between associated function and method).
 
-use hir::{AsAssocItem, InFile, Semantics};
+use hir::{AsAssocItem, InFile, Name, Semantics, sym};
 use ide_db::{
     FileId, FileRange, RootDatabase,
     defs::{Definition, NameClass, NameRefClass},
-    rename::{IdentifierKind, bail, format_err, source_edit_from_references},
+    rename::{IdentifierKind, RenameDefinition, bail, format_err, source_edit_from_references},
     source_change::SourceChangeBuilder,
 };
 use itertools::Itertools;
@@ -33,8 +33,8 @@ pub(crate) fn prepare_rename(
     let source_file = sema.parse_guess_edition(position.file_id);
     let syntax = source_file.syntax();
 
-    let res = find_definitions(&sema, syntax, position)?
-        .map(|(frange, kind, def)| {
+    let res = find_definitions(&sema, syntax, position, &Name::new_symbol_root(sym::underscore))?
+        .map(|(frange, kind, def, _, _)| {
             // ensure all ranges are valid
 
             if def.range_for_rename(&sema).is_none() {
@@ -88,22 +88,28 @@ pub(crate) fn rename(
     let source_file = sema.parse(file_id);
     let syntax = source_file.syntax();
 
-    let defs = find_definitions(&sema, syntax, position)?;
-    let alias_fallback = alias_fallback(syntax, position, new_name);
+    let edition = file_id.edition(db);
+    let (new_name, kind) = IdentifierKind::classify(edition, new_name)?;
+
+    let defs = find_definitions(&sema, syntax, position, &new_name)?;
+    let alias_fallback =
+        alias_fallback(syntax, position, &new_name.display(db, edition).to_string());
 
     let ops: RenameResult<Vec<SourceChange>> = match alias_fallback {
         Some(_) => defs
             // FIXME: This can use the `ide_db::rename_reference` (or def.rename) method once we can
             // properly find "direct" usages/references.
-            .map(|(.., def)| {
-                match IdentifierKind::classify(new_name)? {
+            .map(|(.., def, new_name, _)| {
+                match kind {
                     IdentifierKind::Ident => (),
                     IdentifierKind::Lifetime => {
                         bail!("Cannot alias reference to a lifetime identifier")
                     }
                     IdentifierKind::Underscore => bail!("Cannot alias reference to `_`"),
+                    IdentifierKind::LowercaseSelf => {
+                        bail!("Cannot rename alias reference to `self`")
+                    }
                 };
-
                 let mut usages = def.usages(&sema).all();
 
                 // FIXME: hack - removes the usage that triggered this rename operation.
@@ -120,7 +126,7 @@ pub(crate) fn rename(
                 source_change.extend(usages.references.get_mut(&file_id).iter().map(|refs| {
                     (
                         position.file_id,
-                        source_edit_from_references(refs, def, new_name, file_id.edition(db)),
+                        source_edit_from_references(db, refs, def, &new_name, edition),
                     )
                 }));
 
@@ -128,18 +134,18 @@ pub(crate) fn rename(
             })
             .collect(),
         None => defs
-            .map(|(.., def)| {
+            .map(|(.., def, new_name, rename_def)| {
                 if let Definition::Local(local) = def {
                     if let Some(self_param) = local.as_self_param(sema.db) {
                         cov_mark::hit!(rename_self_to_param);
-                        return rename_self_to_param(&sema, local, self_param, new_name);
+                        return rename_self_to_param(&sema, local, self_param, &new_name, kind);
                     }
-                    if new_name == "self" {
+                    if kind == IdentifierKind::LowercaseSelf {
                         cov_mark::hit!(rename_to_self);
                         return rename_to_self(&sema, local);
                     }
                 }
-                def.rename(&sema, new_name)
+                def.rename(&sema, new_name.as_str(), rename_def)
             })
             .collect(),
     };
@@ -159,7 +165,7 @@ pub(crate) fn will_rename_file(
     let sema = Semantics::new(db);
     let module = sema.file_to_module_def(file_id)?;
     let def = Definition::Module(module);
-    let mut change = def.rename(&sema, new_name_stem).ok()?;
+    let mut change = def.rename(&sema, new_name_stem, RenameDefinition::Yes).ok()?;
     change.file_system_edits.clear();
     Some(change)
 }
@@ -200,22 +206,40 @@ fn find_definitions(
     sema: &Semantics<'_, RootDatabase>,
     syntax: &SyntaxNode,
     FilePosition { file_id, offset }: FilePosition,
-) -> RenameResult<impl Iterator<Item = (FileRange, SyntaxKind, Definition)>> {
-    let token = syntax.token_at_offset(offset).find(|t| matches!(t.kind(), SyntaxKind::STRING));
+    new_name: &Name,
+) -> RenameResult<impl Iterator<Item = (FileRange, SyntaxKind, Definition, Name, RenameDefinition)>>
+{
+    let maybe_format_args =
+        syntax.token_at_offset(offset).find(|t| matches!(t.kind(), SyntaxKind::STRING));
 
     if let Some((range, _, _, Some(resolution))) =
-        token.and_then(|token| sema.check_for_format_args_template(token, offset))
+        maybe_format_args.and_then(|token| sema.check_for_format_args_template(token, offset))
     {
         return Ok(vec![(
             FileRange { file_id, range },
             SyntaxKind::STRING,
             Definition::from(resolution),
+            new_name.clone(),
+            RenameDefinition::Yes,
         )]
         .into_iter());
     }
 
+    let original_ident = syntax
+        .token_at_offset(offset)
+        .max_by_key(|t| {
+            t.kind().is_any_identifier() || matches!(t.kind(), SyntaxKind::LIFETIME_IDENT)
+        })
+        .map(|t| {
+            if t.kind() == SyntaxKind::LIFETIME_IDENT {
+                Name::new_lifetime(t.text())
+            } else {
+                Name::new_root(t.text())
+            }
+        })
+        .ok_or_else(|| format_err!("No references found at position"))?;
     let symbols =
-        sema.find_nodes_at_offset_with_descend::<ast::NameLike>(syntax, offset).map(|name_like| {
+        sema.find_namelike_at_offset_with_descend(syntax, offset).map(|name_like| {
             let kind = name_like.syntax().kind();
             let range = sema
                 .original_range_opt(name_like.syntax())
@@ -284,23 +308,28 @@ fn find_definitions(
                         .ok_or_else(|| format_err!("No references found at position"))
                 }
             };
-            res.map(|def| (range, kind, def))
+            res.map(|def| {
+                let n = def.name(sema.db)?;
+                if n == original_ident {
+                    Some((range, kind, def, new_name.clone(), RenameDefinition::Yes))
+                } else if let Some(suffix) =  n.as_str().strip_prefix(original_ident.as_str()) {
+                    Some((range, kind, def, Name::new_root(&format!("{}{suffix}", new_name.as_str())), RenameDefinition::No))
+                } else {
+                     n.as_str().strip_suffix(original_ident.as_str().trim_start_matches('\''))
+                        .map(|prefix| (range, kind, def, Name::new_root(&format!("{prefix}{}", new_name.as_str())), RenameDefinition::No))
+                }
+            })
         });
 
-    let res: RenameResult<Vec<_>> = symbols.collect();
+    let res: RenameResult<Vec<_>> = symbols.filter_map(Result::transpose).collect();
     match res {
         Ok(v) => {
-            if v.is_empty() {
-                // FIXME: some semantic duplication between "empty vec" and "Err()"
-                Err(format_err!("No references found at position"))
-            } else {
-                // remove duplicates, comparing `Definition`s
-                Ok(v.into_iter()
-                    .unique_by(|&(.., def)| def)
-                    .map(|(a, b, c)| (a.into_file_id(sema.db), b, c))
-                    .collect::<Vec<_>>()
-                    .into_iter())
-            }
+            // remove duplicates, comparing `Definition`s
+            Ok(v.into_iter()
+                .unique_by(|&(.., def, _, _)| def)
+                .map(|(a, b, c, d, e)| (a.into_file_id(sema.db), b, c, d, e))
+                .collect::<Vec<_>>()
+                .into_iter())
         }
         Err(e) => Err(e),
     }
@@ -370,7 +399,13 @@ fn rename_to_self(
     source_change.extend(usages.iter().map(|(file_id, references)| {
         (
             file_id.file_id(sema.db),
-            source_edit_from_references(references, def, "self", file_id.edition(sema.db)),
+            source_edit_from_references(
+                sema.db,
+                references,
+                def,
+                &Name::new_symbol_root(sym::self_),
+                file_id.edition(sema.db),
+            ),
         )
     }));
     source_change.insert_source_edit(
@@ -384,23 +419,25 @@ fn rename_self_to_param(
     sema: &Semantics<'_, RootDatabase>,
     local: hir::Local,
     self_param: hir::SelfParam,
-    new_name: &str,
+    new_name: &Name,
+    identifier_kind: IdentifierKind,
 ) -> RenameResult<SourceChange> {
-    if new_name == "self" {
+    if identifier_kind == IdentifierKind::LowercaseSelf {
         // Let's do nothing rather than complain.
         cov_mark::hit!(rename_self_to_self);
         return Ok(SourceChange::default());
     }
 
-    let identifier_kind = IdentifierKind::classify(new_name)?;
-
     let InFile { file_id, value: self_param } =
         sema.source(self_param).ok_or_else(|| format_err!("cannot find function source"))?;
 
     let def = Definition::Local(local);
     let usages = def.usages(sema).all();
-    let edit = text_edit_from_self_param(&self_param, new_name)
-        .ok_or_else(|| format_err!("No target type found"))?;
+    let edit = text_edit_from_self_param(
+        &self_param,
+        new_name.display(sema.db, file_id.edition(sema.db)).to_string(),
+    )
+    .ok_or_else(|| format_err!("No target type found"))?;
     if usages.len() > 1 && identifier_kind == IdentifierKind::Underscore {
         bail!("Cannot rename reference to `_` as it is being referenced multiple times");
     }
@@ -409,13 +446,19 @@ fn rename_self_to_param(
     source_change.extend(usages.iter().map(|(file_id, references)| {
         (
             file_id.file_id(sema.db),
-            source_edit_from_references(references, def, new_name, file_id.edition(sema.db)),
+            source_edit_from_references(
+                sema.db,
+                references,
+                def,
+                new_name,
+                file_id.edition(sema.db),
+            ),
         )
     }));
     Ok(source_change)
 }
 
-fn text_edit_from_self_param(self_param: &ast::SelfParam, new_name: &str) -> Option<TextEdit> {
+fn text_edit_from_self_param(self_param: &ast::SelfParam, new_name: String) -> Option<TextEdit> {
     fn target_type_name(impl_def: &ast::Impl) -> Option<String> {
         if let Some(ast::Type::PathType(p)) = impl_def.self_ty() {
             return Some(p.path()?.segment()?.name_ref()?.text().to_string());
@@ -427,7 +470,7 @@ fn text_edit_from_self_param(self_param: &ast::SelfParam, new_name: &str) -> Opt
         Some(impl_def) => {
             let type_name = target_type_name(&impl_def)?;
 
-            let mut replacement_text = String::from(new_name);
+            let mut replacement_text = new_name;
             replacement_text.push_str(": ");
             match (self_param.amp_token(), self_param.mut_token()) {
                 (Some(_), None) => replacement_text.push('&'),
@@ -440,7 +483,7 @@ fn text_edit_from_self_param(self_param: &ast::SelfParam, new_name: &str) -> Opt
         }
         None => {
             cov_mark::hit!(rename_self_outside_of_methods);
-            let mut replacement_text = String::from(new_name);
+            let mut replacement_text = new_name;
             replacement_text.push_str(": _");
             Some(TextEdit::replace(self_param.syntax().text_range(), replacement_text))
         }
@@ -710,7 +753,7 @@ impl Foo {
         check(
             "super",
             r#"fn main() { let i$0 = 1; }"#,
-            "error: Invalid name `super`: not an identifier",
+            "error: Invalid name `super`: cannot rename to a keyword",
         );
     }
 
@@ -759,7 +802,11 @@ impl Foo {
 
     #[test]
     fn test_rename_mod_invalid_raw_ident() {
-        check("r#self", r#"mod foo$0 {}"#, "error: Invalid name `self`: not an identifier");
+        check(
+            "r#self",
+            r#"mod foo$0 {}"#,
+            "error: Invalid name `self`: cannot rename module to self",
+        );
     }
 
     #[test]
@@ -2359,7 +2406,6 @@ fn foo(foo: Foo) {
 
     #[test]
     fn test_rename_lifetimes() {
-        cov_mark::check!(rename_lifetime);
         check(
             "'yeeee",
             r#"
@@ -2536,7 +2582,7 @@ fn baz() {
     x.0$0 = 5;
 }
 "#,
-            "error: No identifier available to rename",
+            "error: No references found at position",
         );
     }
 
@@ -2566,7 +2612,7 @@ impl Foo {
     }
 }
 "#,
-            "error: Cannot rename `Self`",
+            "error: No references found at position",
         );
     }
 
@@ -3262,4 +3308,100 @@ trait Trait<U> {
 "#,
         );
     }
+
+    #[test]
+    fn rename_macro_generated_type_from_type_with_a_suffix() {
+        check(
+            "Bar",
+            r#"
+//- proc_macros: generate_suffixed_type
+#[proc_macros::generate_suffixed_type]
+struct Foo$0;
+fn usage(_: FooSuffix) {}
+usage(FooSuffix);
+"#,
+            r#"
+#[proc_macros::generate_suffixed_type]
+struct Bar;
+fn usage(_: BarSuffix) {}
+usage(BarSuffix);
+"#,
+        );
+    }
+
+    #[test]
+    // FIXME
+    #[should_panic]
+    fn rename_macro_generated_type_from_type_usage_with_a_suffix() {
+        check(
+            "Bar",
+            r#"
+//- proc_macros: generate_suffixed_type
+#[proc_macros::generate_suffixed_type]
+struct Foo;
+fn usage(_: FooSuffix) {}
+usage(FooSuffix);
+fn other_place() { Foo$0; }
+"#,
+            r#"
+#[proc_macros::generate_suffixed_type]
+struct Bar;
+fn usage(_: BarSuffix) {}
+usage(BarSuffix);
+fn other_place() { Bar; }
+"#,
+        );
+    }
+
+    #[test]
+    fn rename_macro_generated_type_from_variant_with_a_suffix() {
+        check(
+            "Bar",
+            r#"
+//- proc_macros: generate_suffixed_type
+#[proc_macros::generate_suffixed_type]
+enum Quux {
+    Foo$0,
+}
+fn usage(_: FooSuffix) {}
+usage(FooSuffix);
+"#,
+            r#"
+#[proc_macros::generate_suffixed_type]
+enum Quux {
+    Bar,
+}
+fn usage(_: BarSuffix) {}
+usage(BarSuffix);
+"#,
+        );
+    }
+
+    #[test]
+    // FIXME
+    #[should_panic]
+    fn rename_macro_generated_type_from_variant_usage_with_a_suffix() {
+        check(
+            "Bar",
+            r#"
+//- proc_macros: generate_suffixed_type
+#[proc_macros::generate_suffixed_type]
+enum Quux {
+    Foo,
+}
+fn usage(_: FooSuffix) {}
+usage(FooSuffix);
+fn other_place() { Quux::Foo$0; }
+"#,
+            r#"
+#[proc_macros::generate_suffixed_type]
+enum Quux {
+    Bar,
+}
+fn usage(_: BarSuffix) {}
+usage(BartSuffix);
+fn other_place() { Quux::Bar$0; }
+"#,
+        );
+    }
 }
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 8cc332d4633..8ed0fc6729f 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
@@ -562,8 +562,12 @@ fn closure_expr(p: &mut Parser<'_>) -> CompletedMarker {
 
     let m = p.start();
 
+    // test closure_binder
+    // fn main() { for<'a> || (); }
     if p.at(T![for]) {
+        let b = p.start();
         types::for_binder(p);
+        b.complete(p, CLOSURE_BINDER);
     }
     // test const_closure
     // fn main() { let cl = const || _ = 0; }
diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/generic_params.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/generic_params.rs
index 9d4fdbfaf2e..ea5a3bc8593 100644
--- a/src/tools/rust-analyzer/crates/parser/src/grammar/generic_params.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/grammar/generic_params.rs
@@ -201,6 +201,17 @@ fn type_bound(p: &mut Parser<'_>) -> bool {
             }
             if paths::is_use_path_start(p) {
                 types::path_type_bounds(p, false);
+                // test_err type_bounds_macro_call_recovery
+                // fn foo<T: T![], T: T!, T: T!{}>() -> Box<T! + T!{}> {}
+                if p.at(T![!]) {
+                    let m = p.start();
+                    p.bump(T![!]);
+                    p.error("unexpected `!` in type path, macro calls are not allowed here");
+                    if p.at_ts(TokenSet::new(&[T!['{'], T!['['], T!['(']])) {
+                        items::token_tree(p);
+                    }
+                    m.complete(p, ERROR);
+                }
             } else {
                 m.abandon(p);
                 return false;
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 770827c6b0d..dfe7cb57d24 100644
--- a/src/tools/rust-analyzer/crates/parser/src/grammar/paths.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/grammar/paths.rs
@@ -89,19 +89,22 @@ fn path_segment(p: &mut Parser<'_>, mode: Mode, first: bool) -> Option<Completed
     // test qual_paths
     // type X = <A as B>::Output;
     // fn foo() { <usize as Default>::default(); }
-    if first && p.eat(T![<]) {
+    if first && p.at(T![<]) {
+        let m = p.start();
+        p.bump(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) {
-                types::path_type(p);
+                types::path_type_bounds(p, true);
             } else {
                 p.error("expected a trait");
             }
         }
         p.expect(T![>]);
+        m.complete(p, TYPE_ANCHOR);
         if !p.at(T![::]) {
             p.error("expected `::`");
         }
diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/types.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/types.rs
index 9d31e435cf9..908440b5d05 100644
--- a/src/tools/rust-analyzer/crates/parser/src/grammar/types.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/grammar/types.rs
@@ -330,15 +330,6 @@ fn bare_dyn_trait_type(p: &mut Parser<'_>) {
     m.complete(p, DYN_TRAIT_TYPE);
 }
 
-// test path_type
-// type A = Foo;
-// type B = ::Foo;
-// type C = self::Foo;
-// type D = super::Foo;
-pub(super) fn path_type(p: &mut Parser<'_>) {
-    path_type_bounds(p, true);
-}
-
 // test macro_call_type
 // type A = foo!();
 // type B = crate::foo!();
@@ -365,6 +356,11 @@ fn path_or_macro_type(p: &mut Parser<'_>, allow_bounds: bool) {
     }
 }
 
+// test path_type
+// type A = Foo;
+// type B = ::Foo;
+// type C = self::Foo;
+// type D = super::Foo;
 pub(super) fn path_type_bounds(p: &mut Parser<'_>, allow_bounds: bool) {
     assert!(paths::is_path_start(p));
     let m = p.start();
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 b1727509b13..f534546ea07 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
@@ -291,6 +291,7 @@ pub enum SyntaxKind {
     TUPLE_STRUCT_PAT,
     TUPLE_TYPE,
     TYPE_ALIAS,
+    TYPE_ANCHOR,
     TYPE_ARG,
     TYPE_BOUND,
     TYPE_BOUND_LIST,
@@ -463,6 +464,7 @@ impl SyntaxKind {
             | TUPLE_STRUCT_PAT
             | TUPLE_TYPE
             | TYPE_ALIAS
+            | TYPE_ANCHOR
             | TYPE_ARG
             | TYPE_BOUND
             | TYPE_BOUND_LIST
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs b/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs
index 030d8e0f04d..6ec4192830b 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs
+++ b/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs
@@ -83,6 +83,10 @@ mod ok {
     #[test]
     fn cast_expr() { run_and_expect_no_errors("test_data/parser/inline/ok/cast_expr.rs"); }
     #[test]
+    fn closure_binder() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/closure_binder.rs");
+    }
+    #[test]
     fn closure_body_underscore_assignment() {
         run_and_expect_no_errors(
             "test_data/parser/inline/ok/closure_body_underscore_assignment.rs",
@@ -872,6 +876,10 @@ mod err {
         run_and_expect_errors("test_data/parser/inline/err/tuple_pat_leading_comma.rs");
     }
     #[test]
+    fn type_bounds_macro_call_recovery() {
+        run_and_expect_errors("test_data/parser/inline/err/type_bounds_macro_call_recovery.rs");
+    }
+    #[test]
     fn type_in_array_recover() {
         run_and_expect_errors("test_data/parser/inline/err/type_in_array_recover.rs");
     }
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0024_many_type_parens.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0024_many_type_parens.rast
index f0dbc9b1027..025c12e4c2a 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0024_many_type_parens.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0024_many_type_parens.rast
@@ -186,13 +186,14 @@ SOURCE_FILE
               TUPLE_EXPR
                 L_PAREN "("
                 CLOSURE_EXPR
-                  FOR_KW "for"
-                  GENERIC_PARAM_LIST
-                    L_ANGLE "<"
-                    LIFETIME_PARAM
-                      LIFETIME
-                        LIFETIME_IDENT "'a"
-                    R_ANGLE ">"
+                  CLOSURE_BINDER
+                    FOR_KW "for"
+                    GENERIC_PARAM_LIST
+                      L_ANGLE "<"
+                      LIFETIME_PARAM
+                        LIFETIME
+                          LIFETIME_IDENT "'a"
+                      R_ANGLE ">"
                 WHITESPACE " "
                 BIN_EXPR
                   BIN_EXPR
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/angled_path_without_qual.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/angled_path_without_qual.rast
index 0529e9750e7..53fbe0b615e 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/angled_path_without_qual.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/angled_path_without_qual.rast
@@ -10,11 +10,12 @@ SOURCE_FILE
     PATH_TYPE
       PATH
         PATH_SEGMENT
-          L_ANGLE "<"
-          TUPLE_TYPE
-            L_PAREN "("
-            R_PAREN ")"
-          R_ANGLE ">"
+          TYPE_ANCHOR
+            L_ANGLE "<"
+            TUPLE_TYPE
+              L_PAREN "("
+              R_PAREN ")"
+            R_ANGLE ">"
     SEMICOLON ";"
   WHITESPACE "\n"
   TYPE_ALIAS
@@ -28,21 +29,22 @@ SOURCE_FILE
     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 ">"
+          TYPE_ANCHOR
+            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 `::`
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/type_bounds_macro_call_recovery.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/type_bounds_macro_call_recovery.rast
new file mode 100644
index 00000000000..4722beb6192
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/type_bounds_macro_call_recovery.rast
@@ -0,0 +1,112 @@
+SOURCE_FILE
+  FN
+    FN_KW "fn"
+    WHITESPACE " "
+    NAME
+      IDENT "foo"
+    GENERIC_PARAM_LIST
+      L_ANGLE "<"
+      TYPE_PARAM
+        NAME
+          IDENT "T"
+        COLON ":"
+        WHITESPACE " "
+        TYPE_BOUND_LIST
+          TYPE_BOUND
+            PATH_TYPE
+              PATH
+                PATH_SEGMENT
+                  NAME_REF
+                    IDENT "T"
+            ERROR
+              BANG "!"
+              TOKEN_TREE
+                L_BRACK "["
+                R_BRACK "]"
+      COMMA ","
+      WHITESPACE " "
+      TYPE_PARAM
+        NAME
+          IDENT "T"
+        COLON ":"
+        WHITESPACE " "
+        TYPE_BOUND_LIST
+          TYPE_BOUND
+            PATH_TYPE
+              PATH
+                PATH_SEGMENT
+                  NAME_REF
+                    IDENT "T"
+            ERROR
+              BANG "!"
+      COMMA ","
+      WHITESPACE " "
+      TYPE_PARAM
+        NAME
+          IDENT "T"
+        COLON ":"
+        WHITESPACE " "
+        TYPE_BOUND_LIST
+          TYPE_BOUND
+            PATH_TYPE
+              PATH
+                PATH_SEGMENT
+                  NAME_REF
+                    IDENT "T"
+            ERROR
+              BANG "!"
+              TOKEN_TREE
+                L_CURLY "{"
+                R_CURLY "}"
+      R_ANGLE ">"
+    PARAM_LIST
+      L_PAREN "("
+      R_PAREN ")"
+    WHITESPACE " "
+    RET_TYPE
+      THIN_ARROW "->"
+      WHITESPACE " "
+      PATH_TYPE
+        PATH
+          PATH_SEGMENT
+            NAME_REF
+              IDENT "Box"
+            GENERIC_ARG_LIST
+              L_ANGLE "<"
+              TYPE_ARG
+                DYN_TRAIT_TYPE
+                  TYPE_BOUND_LIST
+                    TYPE_BOUND
+                      MACRO_TYPE
+                        MACRO_CALL
+                          PATH
+                            PATH_SEGMENT
+                              NAME_REF
+                                IDENT "T"
+                          BANG "!"
+                    WHITESPACE " "
+                    PLUS "+"
+                    WHITESPACE " "
+                    TYPE_BOUND
+                      PATH_TYPE
+                        PATH
+                          PATH_SEGMENT
+                            NAME_REF
+                              IDENT "T"
+                      ERROR
+                        BANG "!"
+                        TOKEN_TREE
+                          L_CURLY "{"
+                          R_CURLY "}"
+              R_ANGLE ">"
+    WHITESPACE " "
+    BLOCK_EXPR
+      STMT_LIST
+        L_CURLY "{"
+        R_CURLY "}"
+  WHITESPACE "\n"
+error 12: unexpected `!` in type path, macro calls are not allowed here
+error 21: unexpected `!` in type path, macro calls are not allowed here
+error 28: unexpected `!` in type path, macro calls are not allowed here
+error 43: expected `{`, `[`, `(`
+error 48: unexpected `!` in type path, macro calls are not allowed here
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/type_bounds_macro_call_recovery.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/type_bounds_macro_call_recovery.rs
new file mode 100644
index 00000000000..517404fdb0e
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/type_bounds_macro_call_recovery.rs
@@ -0,0 +1 @@
+fn foo<T: T![], T: T!, T: T!{}>() -> Box<T! + T!{}> {}
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/call_expr.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/call_expr.rast
index 19cc8d5ac7c..7c1d894f7e4 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/call_expr.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/call_expr.rast
@@ -88,13 +88,14 @@ SOURCE_FILE
                   PATH
                     PATH
                       PATH_SEGMENT
-                        L_ANGLE "<"
-                        PATH_TYPE
-                          PATH
-                            PATH_SEGMENT
-                              NAME_REF
-                                IDENT "Foo"
-                        R_ANGLE ">"
+                        TYPE_ANCHOR
+                          L_ANGLE "<"
+                          PATH_TYPE
+                            PATH
+                              PATH_SEGMENT
+                                NAME_REF
+                                  IDENT "Foo"
+                          R_ANGLE ">"
                     COLON2 "::"
                     PATH_SEGMENT
                       NAME_REF
@@ -119,21 +120,22 @@ SOURCE_FILE
                   PATH
                     PATH
                       PATH_SEGMENT
-                        L_ANGLE "<"
-                        PATH_TYPE
-                          PATH
-                            PATH_SEGMENT
-                              NAME_REF
-                                IDENT "Foo"
-                        WHITESPACE " "
-                        AS_KW "as"
-                        WHITESPACE " "
-                        PATH_TYPE
-                          PATH
-                            PATH_SEGMENT
-                              NAME_REF
-                                IDENT "Trait"
-                        R_ANGLE ">"
+                        TYPE_ANCHOR
+                          L_ANGLE "<"
+                          PATH_TYPE
+                            PATH
+                              PATH_SEGMENT
+                                NAME_REF
+                                  IDENT "Foo"
+                          WHITESPACE " "
+                          AS_KW "as"
+                          WHITESPACE " "
+                          PATH_TYPE
+                            PATH
+                              PATH_SEGMENT
+                                NAME_REF
+                                  IDENT "Trait"
+                          R_ANGLE ">"
                     COLON2 "::"
                     PATH_SEGMENT
                       NAME_REF
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/closure_binder.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/closure_binder.rast
new file mode 100644
index 00000000000..c04dbe1ea0a
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/closure_binder.rast
@@ -0,0 +1,36 @@
+SOURCE_FILE
+  FN
+    FN_KW "fn"
+    WHITESPACE " "
+    NAME
+      IDENT "main"
+    PARAM_LIST
+      L_PAREN "("
+      R_PAREN ")"
+    WHITESPACE " "
+    BLOCK_EXPR
+      STMT_LIST
+        L_CURLY "{"
+        WHITESPACE " "
+        EXPR_STMT
+          CLOSURE_EXPR
+            CLOSURE_BINDER
+              FOR_KW "for"
+              GENERIC_PARAM_LIST
+                L_ANGLE "<"
+                LIFETIME_PARAM
+                  LIFETIME
+                    LIFETIME_IDENT "'a"
+                R_ANGLE ">"
+            WHITESPACE " "
+            PARAM_LIST
+              PIPE "|"
+              PIPE "|"
+            WHITESPACE " "
+            TUPLE_EXPR
+              L_PAREN "("
+              R_PAREN ")"
+          SEMICOLON ";"
+        WHITESPACE " "
+        R_CURLY "}"
+  WHITESPACE "\n"
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/closure_binder.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/closure_binder.rs
new file mode 100644
index 00000000000..a6d8aafb08a
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/closure_binder.rs
@@ -0,0 +1 @@
+fn main() { for<'a> || (); }
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/lambda_expr.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/lambda_expr.rast
index c25ad8430d0..ea401d224e6 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/lambda_expr.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/lambda_expr.rast
@@ -202,13 +202,14 @@ SOURCE_FILE
         WHITESPACE "\n    "
         EXPR_STMT
           CLOSURE_EXPR
-            FOR_KW "for"
-            GENERIC_PARAM_LIST
-              L_ANGLE "<"
-              LIFETIME_PARAM
-                LIFETIME
-                  LIFETIME_IDENT "'a"
-              R_ANGLE ">"
+            CLOSURE_BINDER
+              FOR_KW "for"
+              GENERIC_PARAM_LIST
+                L_ANGLE "<"
+                LIFETIME_PARAM
+                  LIFETIME
+                    LIFETIME_IDENT "'a"
+                R_ANGLE ">"
             WHITESPACE " "
             PARAM_LIST
               PIPE "|"
@@ -222,13 +223,14 @@ SOURCE_FILE
         WHITESPACE "\n    "
         EXPR_STMT
           CLOSURE_EXPR
-            FOR_KW "for"
-            GENERIC_PARAM_LIST
-              L_ANGLE "<"
-              LIFETIME_PARAM
-                LIFETIME
-                  LIFETIME_IDENT "'a"
-              R_ANGLE ">"
+            CLOSURE_BINDER
+              FOR_KW "for"
+              GENERIC_PARAM_LIST
+                L_ANGLE "<"
+                LIFETIME_PARAM
+                  LIFETIME
+                    LIFETIME_IDENT "'a"
+                R_ANGLE ">"
             WHITESPACE " "
             MOVE_KW "move"
             WHITESPACE " "
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/qual_paths.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/qual_paths.rast
index 8c66cfe599f..10f8a6a7516 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/qual_paths.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/qual_paths.rast
@@ -11,21 +11,22 @@ SOURCE_FILE
       PATH
         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 ">"
+            TYPE_ANCHOR
+              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 ">"
         COLON2 "::"
         PATH_SEGMENT
           NAME_REF
@@ -51,21 +52,22 @@ SOURCE_FILE
               PATH
                 PATH
                   PATH_SEGMENT
-                    L_ANGLE "<"
-                    PATH_TYPE
-                      PATH
-                        PATH_SEGMENT
-                          NAME_REF
-                            IDENT "usize"
-                    WHITESPACE " "
-                    AS_KW "as"
-                    WHITESPACE " "
-                    PATH_TYPE
-                      PATH
-                        PATH_SEGMENT
-                          NAME_REF
-                            IDENT "Default"
-                    R_ANGLE ">"
+                    TYPE_ANCHOR
+                      L_ANGLE "<"
+                      PATH_TYPE
+                        PATH
+                          PATH_SEGMENT
+                            NAME_REF
+                              IDENT "usize"
+                      WHITESPACE " "
+                      AS_KW "as"
+                      WHITESPACE " "
+                      PATH_TYPE
+                        PATH
+                          PATH_SEGMENT
+                            NAME_REF
+                              IDENT "Default"
+                      R_ANGLE ">"
                 COLON2 "::"
                 PATH_SEGMENT
                   NAME_REF
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/type_path_in_pattern.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/type_path_in_pattern.rast
index 297f7575ca6..3d27afa5ecd 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/type_path_in_pattern.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/type_path_in_pattern.rast
@@ -19,10 +19,11 @@ SOURCE_FILE
             PATH
               PATH
                 PATH_SEGMENT
-                  L_ANGLE "<"
-                  INFER_TYPE
-                    UNDERSCORE "_"
-                  R_ANGLE ">"
+                  TYPE_ANCHOR
+                    L_ANGLE "<"
+                    INFER_TYPE
+                      UNDERSCORE "_"
+                    R_ANGLE ">"
               COLON2 "::"
               PATH_SEGMENT
                 NAME_REF
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/where_clause.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/where_clause.rast
index a3cbe457e1a..9adfe2caa73 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/where_clause.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/where_clause.rast
@@ -84,21 +84,22 @@ SOURCE_FILE
           PATH
             PATH
               PATH_SEGMENT
-                L_ANGLE "<"
-                PATH_TYPE
-                  PATH
-                    PATH_SEGMENT
-                      NAME_REF
-                        IDENT "T"
-                WHITESPACE " "
-                AS_KW "as"
-                WHITESPACE " "
-                PATH_TYPE
-                  PATH
-                    PATH_SEGMENT
-                      NAME_REF
-                        IDENT "Iterator"
-                R_ANGLE ">"
+                TYPE_ANCHOR
+                  L_ANGLE "<"
+                  PATH_TYPE
+                    PATH
+                      PATH_SEGMENT
+                        NAME_REF
+                          IDENT "T"
+                  WHITESPACE " "
+                  AS_KW "as"
+                  WHITESPACE " "
+                  PATH_TYPE
+                    PATH
+                      PATH_SEGMENT
+                        NAME_REF
+                          IDENT "Iterator"
+                  R_ANGLE ">"
             COLON2 "::"
             PATH_SEGMENT
               NAME_REF
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0036_fully_qualified.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0036_fully_qualified.rast
index 9382020e2f6..2fecb1cc47d 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0036_fully_qualified.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0036_fully_qualified.rast
@@ -45,21 +45,22 @@ SOURCE_FILE
           PATH
             PATH
               PATH_SEGMENT
-                L_ANGLE "<"
-                PATH_TYPE
-                  PATH
-                    PATH_SEGMENT
-                      NAME_REF
-                        IDENT "S"
-                WHITESPACE " "
-                AS_KW "as"
-                WHITESPACE " "
-                PATH_TYPE
-                  PATH
-                    PATH_SEGMENT
-                      NAME_REF
-                        IDENT "Iterator"
-                R_ANGLE ">"
+                TYPE_ANCHOR
+                  L_ANGLE "<"
+                  PATH_TYPE
+                    PATH
+                      PATH_SEGMENT
+                        NAME_REF
+                          IDENT "S"
+                  WHITESPACE " "
+                  AS_KW "as"
+                  WHITESPACE " "
+                  PATH_TYPE
+                    PATH
+                      PATH_SEGMENT
+                        NAME_REF
+                          IDENT "Iterator"
+                  R_ANGLE ">"
             COLON2 "::"
             PATH_SEGMENT
               NAME_REF
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0042_ufcs_call_list.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0042_ufcs_call_list.rast
index a536b0e881f..d1d1ffacf0d 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0042_ufcs_call_list.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0042_ufcs_call_list.rast
@@ -107,13 +107,14 @@ SOURCE_FILE
                 PATH
                   PATH
                     PATH_SEGMENT
-                      L_ANGLE "<"
-                      PATH_TYPE
-                        PATH
-                          PATH_SEGMENT
-                            NAME_REF
-                              IDENT "Foo"
-                      R_ANGLE ">"
+                      TYPE_ANCHOR
+                        L_ANGLE "<"
+                        PATH_TYPE
+                          PATH
+                            PATH_SEGMENT
+                              NAME_REF
+                                IDENT "Foo"
+                        R_ANGLE ">"
                   COLON2 "::"
                   PATH_SEGMENT
                     NAME_REF
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0067_where_for_pred.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0067_where_for_pred.rast
index cd3b21ae94f..8bf1090f9cf 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0067_where_for_pred.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0067_where_for_pred.rast
@@ -288,26 +288,27 @@ SOURCE_FILE
           PATH
             PATH
               PATH_SEGMENT
-                L_ANGLE "<"
-                REF_TYPE
-                  AMP "&"
-                  LIFETIME
-                    LIFETIME_IDENT "'a"
+                TYPE_ANCHOR
+                  L_ANGLE "<"
+                  REF_TYPE
+                    AMP "&"
+                    LIFETIME
+                      LIFETIME_IDENT "'a"
+                    WHITESPACE " "
+                    PATH_TYPE
+                      PATH
+                        PATH_SEGMENT
+                          NAME_REF
+                            IDENT "T"
+                  WHITESPACE " "
+                  AS_KW "as"
                   WHITESPACE " "
                   PATH_TYPE
                     PATH
                       PATH_SEGMENT
                         NAME_REF
-                          IDENT "T"
-                WHITESPACE " "
-                AS_KW "as"
-                WHITESPACE " "
-                PATH_TYPE
-                  PATH
-                    PATH_SEGMENT
-                      NAME_REF
-                        IDENT "Baz"
-                R_ANGLE ">"
+                          IDENT "Baz"
+                  R_ANGLE ">"
             COLON2 "::"
             PATH_SEGMENT
               NAME_REF
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl.rs
index 11dbd920091..ad285990334 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl.rs
@@ -10,6 +10,7 @@
 
 use std::fmt;
 
+use intern::Symbol;
 use proc_macro::bridge;
 
 mod token_stream;
@@ -112,3 +113,135 @@ fn literal_kind_to_internal(kind: bridge::LitKind) -> tt::LitKind {
         bridge::LitKind::ErrWithGuar => tt::LitKind::Err(()),
     }
 }
+
+pub(super) fn literal_from_str<Span: Copy>(
+    s: &str,
+    span: Span,
+) -> Result<bridge::Literal<Span, Symbol>, ()> {
+    use proc_macro::bridge::LitKind;
+    use rustc_lexer::{LiteralKind, Token, TokenKind};
+
+    let mut tokens = rustc_lexer::tokenize(s);
+    let minus_or_lit = tokens.next().unwrap_or(Token { kind: TokenKind::Eof, len: 0 });
+
+    let lit = if minus_or_lit.kind == TokenKind::Minus {
+        let lit = tokens.next().ok_or(())?;
+        if !matches!(
+            lit.kind,
+            TokenKind::Literal { kind: LiteralKind::Int { .. } | LiteralKind::Float { .. }, .. }
+        ) {
+            return Err(());
+        }
+        lit
+    } else {
+        minus_or_lit
+    };
+
+    if tokens.next().is_some() {
+        return Err(());
+    }
+
+    let TokenKind::Literal { kind, suffix_start } = lit.kind else { return Err(()) };
+    let (kind, start_offset, end_offset) = match kind {
+        LiteralKind::Int { .. } => (LitKind::Integer, 0, 0),
+        LiteralKind::Float { .. } => (LitKind::Float, 0, 0),
+        LiteralKind::Char { terminated } => (LitKind::Char, 1, terminated as usize),
+        LiteralKind::Byte { terminated } => (LitKind::Byte, 2, terminated as usize),
+        LiteralKind::Str { terminated } => (LitKind::Str, 1, terminated as usize),
+        LiteralKind::ByteStr { terminated } => (LitKind::ByteStr, 2, terminated as usize),
+        LiteralKind::CStr { terminated } => (LitKind::CStr, 2, terminated as usize),
+        LiteralKind::RawStr { n_hashes } => (
+            LitKind::StrRaw(n_hashes.unwrap_or_default()),
+            2 + n_hashes.unwrap_or_default() as usize,
+            1 + n_hashes.unwrap_or_default() as usize,
+        ),
+        LiteralKind::RawByteStr { n_hashes } => (
+            LitKind::ByteStrRaw(n_hashes.unwrap_or_default()),
+            3 + n_hashes.unwrap_or_default() as usize,
+            1 + n_hashes.unwrap_or_default() as usize,
+        ),
+        LiteralKind::RawCStr { n_hashes } => (
+            LitKind::CStrRaw(n_hashes.unwrap_or_default()),
+            3 + n_hashes.unwrap_or_default() as usize,
+            1 + n_hashes.unwrap_or_default() as usize,
+        ),
+    };
+
+    let (lit, suffix) = s.split_at(suffix_start as usize);
+    let lit = &lit[start_offset..lit.len() - end_offset];
+    let suffix = match suffix {
+        "" | "_" => None,
+        suffix => Some(Symbol::intern(suffix)),
+    };
+
+    Ok(bridge::Literal { kind, symbol: Symbol::intern(lit), suffix, span })
+}
+
+pub(super) fn from_token_tree<Span: Copy>(
+    tree: bridge::TokenTree<TokenStream<Span>, Span, Symbol>,
+) -> TokenStream<Span> {
+    match tree {
+        bridge::TokenTree::Group(group) => {
+            let group = TopSubtree::from_bridge(group);
+            TokenStream { token_trees: group.0 }
+        }
+
+        bridge::TokenTree::Ident(ident) => {
+            let text = ident.sym;
+            let ident: tt::Ident<Span> = tt::Ident {
+                sym: text,
+                span: ident.span,
+                is_raw: if ident.is_raw { tt::IdentIsRaw::Yes } else { tt::IdentIsRaw::No },
+            };
+            let leaf = tt::Leaf::from(ident);
+            let tree = tt::TokenTree::from(leaf);
+            TokenStream { token_trees: vec![tree] }
+        }
+
+        bridge::TokenTree::Literal(literal) => {
+            let token_trees =
+                if let Some((_minus, symbol)) = literal.symbol.as_str().split_once('-') {
+                    let punct = tt::Punct {
+                        spacing: tt::Spacing::Alone,
+                        span: literal.span,
+                        char: '-' as char,
+                    };
+                    let leaf: tt::Leaf<Span> = tt::Leaf::from(punct);
+                    let minus_tree = tt::TokenTree::from(leaf);
+
+                    let literal = tt::Literal {
+                        symbol: Symbol::intern(symbol),
+                        suffix: literal.suffix,
+                        span: literal.span,
+                        kind: literal_kind_to_internal(literal.kind),
+                    };
+                    let leaf: tt::Leaf<Span> = tt::Leaf::from(literal);
+                    let tree = tt::TokenTree::from(leaf);
+                    vec![minus_tree, tree]
+                } else {
+                    let literal = tt::Literal {
+                        symbol: literal.symbol,
+                        suffix: literal.suffix,
+                        span: literal.span,
+                        kind: literal_kind_to_internal(literal.kind),
+                    };
+
+                    let leaf: tt::Leaf<Span> = tt::Leaf::from(literal);
+                    let tree = tt::TokenTree::from(leaf);
+                    vec![tree]
+                };
+            TokenStream { token_trees }
+        }
+
+        bridge::TokenTree::Punct(p) => {
+            let punct = tt::Punct {
+                char: p.ch as char,
+                spacing: if p.joint { tt::Spacing::Joint } else { tt::Spacing::Alone },
+                span: p.span,
+            };
+            let leaf = tt::Leaf::from(punct);
+            let tree = tt::TokenTree::from(leaf);
+            TokenStream { token_trees: vec![tree] }
+        }
+    }
+}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs
index e0c6e68f803..5d1271ba81e 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs
@@ -14,16 +14,7 @@ use proc_macro::bridge::{self, server};
 use span::{FIXUP_ERASED_FILE_AST_ID_MARKER, Span};
 use tt::{TextRange, TextSize};
 
-use crate::server_impl::{TopSubtree, literal_kind_to_internal, token_stream::TokenStreamBuilder};
-mod tt {
-    pub use tt::*;
-
-    pub type TokenTree = ::tt::TokenTree<super::Span>;
-    pub type Leaf = ::tt::Leaf<super::Span>;
-    pub type Literal = ::tt::Literal<super::Span>;
-    pub type Punct = ::tt::Punct<super::Span>;
-    pub type Ident = ::tt::Ident<super::Span>;
-}
+use crate::server_impl::{from_token_tree, literal_from_str, token_stream::TokenStreamBuilder};
 
 type TokenStream = crate::server_impl::TokenStream<Span>;
 
@@ -62,66 +53,7 @@ impl server::FreeFunctions for RaSpanServer {
         &mut self,
         s: &str,
     ) -> Result<bridge::Literal<Self::Span, Self::Symbol>, ()> {
-        use proc_macro::bridge::LitKind;
-        use rustc_lexer::{LiteralKind, Token, TokenKind};
-
-        let mut tokens = rustc_lexer::tokenize(s);
-        let minus_or_lit = tokens.next().unwrap_or(Token { kind: TokenKind::Eof, len: 0 });
-
-        let lit = if minus_or_lit.kind == TokenKind::Minus {
-            let lit = tokens.next().ok_or(())?;
-            if !matches!(
-                lit.kind,
-                TokenKind::Literal {
-                    kind: LiteralKind::Int { .. } | LiteralKind::Float { .. },
-                    ..
-                }
-            ) {
-                return Err(());
-            }
-            lit
-        } else {
-            minus_or_lit
-        };
-
-        if tokens.next().is_some() {
-            return Err(());
-        }
-
-        let TokenKind::Literal { kind, suffix_start } = lit.kind else { return Err(()) };
-        let (kind, start_offset, end_offset) = match kind {
-            LiteralKind::Int { .. } => (LitKind::Integer, 0, 0),
-            LiteralKind::Float { .. } => (LitKind::Float, 0, 0),
-            LiteralKind::Char { terminated } => (LitKind::Char, 1, terminated as usize),
-            LiteralKind::Byte { terminated } => (LitKind::Byte, 2, terminated as usize),
-            LiteralKind::Str { terminated } => (LitKind::Str, 1, terminated as usize),
-            LiteralKind::ByteStr { terminated } => (LitKind::ByteStr, 2, terminated as usize),
-            LiteralKind::CStr { terminated } => (LitKind::CStr, 2, terminated as usize),
-            LiteralKind::RawStr { n_hashes } => (
-                LitKind::StrRaw(n_hashes.unwrap_or_default()),
-                2 + n_hashes.unwrap_or_default() as usize,
-                1 + n_hashes.unwrap_or_default() as usize,
-            ),
-            LiteralKind::RawByteStr { n_hashes } => (
-                LitKind::ByteStrRaw(n_hashes.unwrap_or_default()),
-                3 + n_hashes.unwrap_or_default() as usize,
-                1 + n_hashes.unwrap_or_default() as usize,
-            ),
-            LiteralKind::RawCStr { n_hashes } => (
-                LitKind::CStrRaw(n_hashes.unwrap_or_default()),
-                3 + n_hashes.unwrap_or_default() as usize,
-                1 + n_hashes.unwrap_or_default() as usize,
-            ),
-        };
-
-        let (lit, suffix) = s.split_at(suffix_start as usize);
-        let lit = &lit[start_offset..lit.len() - end_offset];
-        let suffix = match suffix {
-            "" | "_" => None,
-            suffix => Some(Symbol::intern(suffix)),
-        };
-
-        Ok(bridge::Literal { kind, symbol: Symbol::intern(lit), suffix, span: self.call_site })
+        literal_from_str(s, self.call_site)
     }
 
     fn emit_diagnostic(&mut self, _: bridge::Diagnostic<Self::Span>) {
@@ -149,70 +81,7 @@ impl server::TokenStream for RaSpanServer {
         &mut self,
         tree: bridge::TokenTree<Self::TokenStream, Self::Span, Self::Symbol>,
     ) -> Self::TokenStream {
-        match tree {
-            bridge::TokenTree::Group(group) => {
-                let group = TopSubtree::from_bridge(group);
-                TokenStream { token_trees: group.0 }
-            }
-
-            bridge::TokenTree::Ident(ident) => {
-                let text = ident.sym;
-                let ident: tt::Ident = tt::Ident {
-                    sym: text,
-                    span: ident.span,
-                    is_raw: if ident.is_raw { tt::IdentIsRaw::Yes } else { tt::IdentIsRaw::No },
-                };
-                let leaf = tt::Leaf::from(ident);
-                let tree = tt::TokenTree::from(leaf);
-                TokenStream { token_trees: vec![tree] }
-            }
-
-            bridge::TokenTree::Literal(literal) => {
-                let token_trees =
-                    if let Some((_minus, symbol)) = literal.symbol.as_str().split_once('-') {
-                        let punct = tt::Punct {
-                            spacing: tt::Spacing::Alone,
-                            span: literal.span,
-                            char: '-' as char,
-                        };
-                        let leaf: tt::Leaf = tt::Leaf::from(punct);
-                        let minus_tree = tt::TokenTree::from(leaf);
-
-                        let literal = tt::Literal {
-                            symbol: Symbol::intern(symbol),
-                            suffix: literal.suffix,
-                            span: literal.span,
-                            kind: literal_kind_to_internal(literal.kind),
-                        };
-                        let leaf: tt::Leaf = tt::Leaf::from(literal);
-                        let tree = tt::TokenTree::from(leaf);
-                        vec![minus_tree, tree]
-                    } else {
-                        let literal = tt::Literal {
-                            symbol: literal.symbol,
-                            suffix: literal.suffix,
-                            span: literal.span,
-                            kind: literal_kind_to_internal(literal.kind),
-                        };
-
-                        let leaf: tt::Leaf = tt::Leaf::from(literal);
-                        let tree = tt::TokenTree::from(leaf);
-                        vec![tree]
-                    };
-                TokenStream { token_trees }
-            }
-
-            bridge::TokenTree::Punct(p) => {
-                let punct = tt::Punct {
-                    char: p.ch as char,
-                    spacing: if p.joint { tt::Spacing::Joint } else { tt::Spacing::Alone },
-                    span: p.span,
-                };
-                let leaf = tt::Leaf::from(punct);
-                let tree = tt::TokenTree::from(leaf);
-                TokenStream { token_trees: vec![tree] }
-            }
-        }
+        from_token_tree(tree)
     }
 
     fn expand_expr(&mut self, self_: &Self::TokenStream) -> Result<Self::TokenStream, ()> {
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs
index d55b269f868..b493b325e83 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs
@@ -5,23 +5,9 @@ use std::ops::{Bound, Range};
 use intern::Symbol;
 use proc_macro::bridge::{self, server};
 
-use crate::server_impl::{TopSubtree, literal_kind_to_internal, token_stream::TokenStreamBuilder};
-mod tt {
-    pub use span::TokenId;
+use crate::server_impl::{from_token_tree, literal_from_str, token_stream::TokenStreamBuilder};
 
-    pub use tt::*;
-
-    pub type TokenTree = ::tt::TokenTree<TokenId>;
-    pub type Leaf = ::tt::Leaf<TokenId>;
-    pub type Literal = ::tt::Literal<TokenId>;
-    pub type Punct = ::tt::Punct<TokenId>;
-    pub type Ident = ::tt::Ident<TokenId>;
-}
-type TokenTree = tt::TokenTree;
-type Punct = tt::Punct;
-type Spacing = tt::Spacing;
-type Literal = tt::Literal;
-type Span = tt::TokenId;
+type Span = span::TokenId;
 type TokenStream = crate::server_impl::TokenStream<Span>;
 
 pub struct FreeFunctions;
@@ -49,67 +35,7 @@ impl server::FreeFunctions for TokenIdServer {
         &mut self,
         s: &str,
     ) -> Result<bridge::Literal<Self::Span, Self::Symbol>, ()> {
-        use proc_macro::bridge::LitKind;
-        use rustc_lexer::{LiteralKind, Token, TokenKind};
-
-        let mut tokens = rustc_lexer::tokenize(s);
-        let minus_or_lit = tokens.next().unwrap_or(Token { kind: TokenKind::Eof, len: 0 });
-
-        let lit = if minus_or_lit.kind == TokenKind::Minus {
-            let lit = tokens.next().ok_or(())?;
-            if !matches!(
-                lit.kind,
-                TokenKind::Literal {
-                    kind: LiteralKind::Int { .. } | LiteralKind::Float { .. },
-                    ..
-                }
-            ) {
-                return Err(());
-            }
-            lit
-        } else {
-            minus_or_lit
-        };
-
-        if tokens.next().is_some() {
-            return Err(());
-        }
-
-        let TokenKind::Literal { kind, suffix_start } = lit.kind else { return Err(()) };
-
-        let (kind, start_offset, end_offset) = match kind {
-            LiteralKind::Int { .. } => (LitKind::Integer, 0, 0),
-            LiteralKind::Float { .. } => (LitKind::Float, 0, 0),
-            LiteralKind::Char { terminated } => (LitKind::Char, 1, terminated as usize),
-            LiteralKind::Byte { terminated } => (LitKind::Byte, 2, terminated as usize),
-            LiteralKind::Str { terminated } => (LitKind::Str, 1, terminated as usize),
-            LiteralKind::ByteStr { terminated } => (LitKind::ByteStr, 2, terminated as usize),
-            LiteralKind::CStr { terminated } => (LitKind::CStr, 2, terminated as usize),
-            LiteralKind::RawStr { n_hashes } => (
-                LitKind::StrRaw(n_hashes.unwrap_or_default()),
-                2 + n_hashes.unwrap_or_default() as usize,
-                1 + n_hashes.unwrap_or_default() as usize,
-            ),
-            LiteralKind::RawByteStr { n_hashes } => (
-                LitKind::ByteStrRaw(n_hashes.unwrap_or_default()),
-                3 + n_hashes.unwrap_or_default() as usize,
-                1 + n_hashes.unwrap_or_default() as usize,
-            ),
-            LiteralKind::RawCStr { n_hashes } => (
-                LitKind::CStrRaw(n_hashes.unwrap_or_default()),
-                3 + n_hashes.unwrap_or_default() as usize,
-                1 + n_hashes.unwrap_or_default() as usize,
-            ),
-        };
-
-        let (lit, suffix) = s.split_at(suffix_start as usize);
-        let lit = &lit[start_offset..lit.len() - end_offset];
-        let suffix = match suffix {
-            "" | "_" => None,
-            suffix => Some(Symbol::intern(suffix)),
-        };
-
-        Ok(bridge::Literal { kind, symbol: Symbol::intern(lit), suffix, span: self.call_site })
+        literal_from_str(s, self.call_site)
     }
 
     fn emit_diagnostic(&mut self, _: bridge::Diagnostic<Self::Span>) {}
@@ -135,69 +61,7 @@ impl server::TokenStream for TokenIdServer {
         &mut self,
         tree: bridge::TokenTree<Self::TokenStream, Self::Span, Self::Symbol>,
     ) -> Self::TokenStream {
-        match tree {
-            bridge::TokenTree::Group(group) => {
-                let group = TopSubtree::from_bridge(group);
-                TokenStream { token_trees: group.0 }
-            }
-
-            bridge::TokenTree::Ident(ident) => {
-                let ident: tt::Ident = tt::Ident {
-                    sym: ident.sym,
-                    span: ident.span,
-                    is_raw: if ident.is_raw { tt::IdentIsRaw::Yes } else { tt::IdentIsRaw::No },
-                };
-                let leaf = tt::Leaf::from(ident);
-                let tree = TokenTree::from(leaf);
-                TokenStream { token_trees: vec![tree] }
-            }
-
-            bridge::TokenTree::Literal(literal) => {
-                let token_trees =
-                    if let Some((_minus, symbol)) = literal.symbol.as_str().split_once('-') {
-                        let punct = tt::Punct {
-                            spacing: tt::Spacing::Alone,
-                            span: literal.span,
-                            char: '-' as char,
-                        };
-                        let leaf: tt::Leaf = tt::Leaf::from(punct);
-                        let minus_tree = tt::TokenTree::from(leaf);
-
-                        let literal = Literal {
-                            symbol: Symbol::intern(symbol),
-                            suffix: literal.suffix,
-                            span: literal.span,
-                            kind: literal_kind_to_internal(literal.kind),
-                        };
-                        let leaf: tt::Leaf = tt::Leaf::from(literal);
-                        let tree = tt::TokenTree::from(leaf);
-                        vec![minus_tree, tree]
-                    } else {
-                        let literal = Literal {
-                            symbol: literal.symbol,
-                            suffix: literal.suffix,
-                            span: literal.span,
-                            kind: literal_kind_to_internal(literal.kind),
-                        };
-
-                        let leaf: tt::Leaf = tt::Leaf::from(literal);
-                        let tree = tt::TokenTree::from(leaf);
-                        vec![tree]
-                    };
-                TokenStream { token_trees }
-            }
-
-            bridge::TokenTree::Punct(p) => {
-                let punct = Punct {
-                    char: p.ch as char,
-                    spacing: if p.joint { Spacing::Joint } else { Spacing::Alone },
-                    span: p.span,
-                };
-                let leaf = tt::Leaf::from(punct);
-                let tree = TokenTree::from(leaf);
-                TokenStream { token_trees: vec![tree] }
-            }
-        }
+        from_token_tree(tree)
     }
 
     fn expand_expr(&mut self, self_: &Self::TokenStream) -> Result<Self::TokenStream, ()> {
@@ -337,6 +201,8 @@ impl server::Server for TokenIdServer {
 
 #[cfg(test)]
 mod tests {
+    use span::TokenId;
+
     use super::*;
 
     #[test]
@@ -345,18 +211,18 @@ mod tests {
             token_trees: vec![
                 tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident {
                     sym: Symbol::intern("struct"),
-                    span: tt::TokenId(0),
+                    span: TokenId(0),
                     is_raw: tt::IdentIsRaw::No,
                 })),
                 tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident {
                     sym: Symbol::intern("T"),
-                    span: tt::TokenId(0),
+                    span: TokenId(0),
                     is_raw: tt::IdentIsRaw::No,
                 })),
                 tt::TokenTree::Subtree(tt::Subtree {
                     delimiter: tt::Delimiter {
-                        open: tt::TokenId(0),
-                        close: tt::TokenId(0),
+                        open: TokenId(0),
+                        close: TokenId(0),
                         kind: tt::DelimiterKind::Brace,
                     },
                     len: 0,
@@ -372,8 +238,8 @@ mod tests {
         let subtree_paren_a = vec![
             tt::TokenTree::Subtree(tt::Subtree {
                 delimiter: tt::Delimiter {
-                    open: tt::TokenId(0),
-                    close: tt::TokenId(0),
+                    open: TokenId(0),
+                    close: TokenId(0),
                     kind: tt::DelimiterKind::Parenthesis,
                 },
                 len: 1,
@@ -381,24 +247,24 @@ mod tests {
             tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident {
                 is_raw: tt::IdentIsRaw::No,
                 sym: Symbol::intern("a"),
-                span: tt::TokenId(0),
+                span: TokenId(0),
             })),
         ];
 
-        let t1 = TokenStream::from_str("(a)", tt::TokenId(0)).unwrap();
+        let t1 = TokenStream::from_str("(a)", TokenId(0)).unwrap();
         assert_eq!(t1.token_trees.len(), 2);
         assert!(t1.token_trees[0..2] == subtree_paren_a);
 
-        let t2 = TokenStream::from_str("(a);", tt::TokenId(0)).unwrap();
+        let t2 = TokenStream::from_str("(a);", TokenId(0)).unwrap();
         assert_eq!(t2.token_trees.len(), 3);
         assert!(t2.token_trees[0..2] == subtree_paren_a);
 
-        let underscore = TokenStream::from_str("_", tt::TokenId(0)).unwrap();
+        let underscore = TokenStream::from_str("_", TokenId(0)).unwrap();
         assert!(
             underscore.token_trees[0]
                 == tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident {
                     sym: Symbol::intern("_"),
-                    span: tt::TokenId(0),
+                    span: TokenId(0),
                     is_raw: tt::IdentIsRaw::No,
                 }))
         );
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 6e730b1aea2..bb02284a513 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
@@ -431,12 +431,6 @@ impl CargoWorkspace {
                 .ok_or(cargo_metadata::Error::NoJson)?;
             Ok((cargo_metadata::MetadataCommand::parse(stdout)?, None))
         })()
-        .map(|(metadata, error)| {
-            (
-                metadata,
-                error.map(|e| e.context(format!("Failed to run `{:?}`", meta.cargo_command()))),
-            )
-        })
         .with_context(|| format!("Failed to run `{:?}`", meta.cargo_command()))
     }
 
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 d1ca8c1a91a..5cbea9c2b3d 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
@@ -1192,7 +1192,7 @@ impl ConfigChange {
 #[derive(Debug, Clone, Eq, PartialEq)]
 pub enum LinkedProject {
     ProjectManifest(ProjectManifest),
-    InlineJsonProject(ProjectJson),
+    InlineProjectJson(ProjectJson),
 }
 
 impl From<ProjectManifest> for LinkedProject {
@@ -1203,7 +1203,7 @@ impl From<ProjectManifest> for LinkedProject {
 
 impl From<ProjectJson> for LinkedProject {
     fn from(v: ProjectJson) -> Self {
-        LinkedProject::InlineJsonProject(v)
+        LinkedProject::InlineProjectJson(v)
     }
 }
 
@@ -1597,6 +1597,16 @@ impl Config {
             term_search_borrowck: self.assist_termSearch_borrowcheck(source_root).to_owned(),
         }
     }
+
+    pub fn diagnostic_fixes(&self, source_root: Option<SourceRootId>) -> DiagnosticsConfig {
+        // We always want to show quickfixes for diagnostics, even when diagnostics/experimental diagnostics are disabled.
+        DiagnosticsConfig {
+            enabled: true,
+            disable_experimental: false,
+            ..self.diagnostics(source_root)
+        }
+    }
+
     pub fn expand_proc_attr_macros(&self) -> bool {
         self.procMacro_enable().to_owned() && self.procMacro_attributes_enable().to_owned()
     }
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs
index 69983a67626..6d46ce68ed4 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs
@@ -1439,7 +1439,7 @@ pub(crate) fn handle_code_action(
     };
     let assists = snap.analysis.assists_with_fixes(
         &assists_config,
-        &snap.config.diagnostics(Some(source_root)),
+        &snap.config.diagnostic_fixes(Some(source_root)),
         resolve,
         frange,
     )?;
@@ -1530,7 +1530,7 @@ pub(crate) fn handle_code_action_resolve(
 
     let assists = snap.analysis.assists_with_fixes(
         &assists_config,
-        &snap.config.diagnostics(Some(source_root)),
+        &snap.config.diagnostic_fixes(Some(source_root)),
         AssistResolveStrategy::Single(assist_resolve),
         frange,
     )?;
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/utils.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/utils.rs
index 673eaa5952f..5bea7084fdb 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/utils.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/utils.rs
@@ -108,8 +108,7 @@ impl GlobalState {
     /// edge users from being upset!
     pub(crate) fn poke_rust_analyzer_developer(&mut self, message: String) {
         let from_source_build = option_env!("POKE_RA_DEVS").is_some();
-        let profiling_enabled = std::env::var("RA_PROFILE").is_ok();
-        if from_source_build || profiling_enabled {
+        if from_source_build {
             self.show_and_log_error(message, None);
         }
     }
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 ae9e3e99874..4677880daaf 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
@@ -292,7 +292,7 @@ impl GlobalState {
 
                 if let (Some(_command), Some(path)) = (&discover_command, &path) {
                     let build = linked_projects.iter().find_map(|project| match project {
-                        LinkedProject::InlineJsonProject(it) => it.crate_by_buildfile(path),
+                        LinkedProject::InlineProjectJson(it) => it.crate_by_buildfile(path),
                         _ => None,
                     });
 
@@ -318,7 +318,7 @@ impl GlobalState {
                                 &progress,
                             )
                         }
-                        LinkedProject::InlineJsonProject(it) => {
+                        LinkedProject::InlineProjectJson(it) => {
                             let workspace = project_model::ProjectWorkspace::load_inline(
                                 it.clone(),
                                 &cargo_config,
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs
index f6bcb5642c3..59073af983b 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs
@@ -975,10 +975,6 @@ version = \"0.0.0\"
 }
 
 fn out_dirs_check_impl(root_contains_symlink: bool) {
-    if skip_slow_tests() {
-        return;
-    }
-
     let mut server = Project::with_fixture(
         r###"
 //- /Cargo.toml
@@ -1130,12 +1126,18 @@ fn main() {
 
 #[test]
 fn out_dirs_check() {
+    if skip_slow_tests() {
+        return;
+    }
     out_dirs_check_impl(false);
 }
 
 #[test]
 #[cfg(not(windows))] // windows requires elevated permissions to create symlinks
 fn root_contains_symlink_out_dirs_check() {
+    if skip_slow_tests() {
+        return;
+    }
     out_dirs_check_impl(true);
 }
 
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/ratoml.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/ratoml.rs
index 485f32281dd..cac7efd84aa 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/ratoml.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/ratoml.rs
@@ -439,6 +439,7 @@ assist.emitMustUse = true"#,
 }
 
 #[test]
+#[ignore = "flaky test that tends to hang"]
 fn ratoml_inherit_config_from_ws_root() {
     if skip_slow_tests() {
         return;
diff --git a/src/tools/rust-analyzer/crates/syntax/rust.ungram b/src/tools/rust-analyzer/crates/syntax/rust.ungram
index 10abca7d35d..c81da06682e 100644
--- a/src/tools/rust-analyzer/crates/syntax/rust.ungram
+++ b/src/tools/rust-analyzer/crates/syntax/rust.ungram
@@ -39,7 +39,10 @@ PathSegment =
 | NameRef GenericArgList?
 | NameRef ParenthesizedArgList RetType?
 | NameRef ReturnTypeSyntax
-| '<' Type ('as' PathType)? '>'
+| TypeAnchor
+
+TypeAnchor =
+  '<' Type ('as' PathType)? '>'
 
 ReturnTypeSyntax =
   '(' '..' ')'
@@ -98,7 +101,7 @@ WhereClause =
   'where' predicates:(WherePred (',' WherePred)* ','?)
 
 WherePred =
-  ('for' GenericParamList)?  (Lifetime | Type) ':' TypeBoundList?
+  ('for' GenericParamList)? (Lifetime | Type) ':' TypeBoundList?
 
 
 //*************************//
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 cd9f4dba890..04c7e8a578c 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
@@ -1232,21 +1232,13 @@ impl PathSegment {
         support::child(&self.syntax)
     }
     #[inline]
-    pub fn path_type(&self) -> Option<PathType> { support::child(&self.syntax) }
-    #[inline]
     pub fn ret_type(&self) -> Option<RetType> { support::child(&self.syntax) }
     #[inline]
     pub fn return_type_syntax(&self) -> Option<ReturnTypeSyntax> { support::child(&self.syntax) }
     #[inline]
-    pub fn ty(&self) -> Option<Type> { support::child(&self.syntax) }
+    pub fn type_anchor(&self) -> Option<TypeAnchor> { support::child(&self.syntax) }
     #[inline]
     pub fn coloncolon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![::]) }
-    #[inline]
-    pub fn l_angle_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![<]) }
-    #[inline]
-    pub fn r_angle_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![>]) }
-    #[inline]
-    pub fn as_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![as]) }
 }
 pub struct PathType {
     pub(crate) syntax: SyntaxNode,
@@ -1739,6 +1731,21 @@ impl TypeAlias {
     #[inline]
     pub fn type_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![type]) }
 }
+pub struct TypeAnchor {
+    pub(crate) syntax: SyntaxNode,
+}
+impl TypeAnchor {
+    #[inline]
+    pub fn path_type(&self) -> Option<PathType> { support::child(&self.syntax) }
+    #[inline]
+    pub fn ty(&self) -> Option<Type> { support::child(&self.syntax) }
+    #[inline]
+    pub fn l_angle_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![<]) }
+    #[inline]
+    pub fn r_angle_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![>]) }
+    #[inline]
+    pub fn as_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![as]) }
+}
 pub struct TypeArg {
     pub(crate) syntax: SyntaxNode,
 }
@@ -7108,6 +7115,42 @@ impl fmt::Debug for TypeAlias {
         f.debug_struct("TypeAlias").field("syntax", &self.syntax).finish()
     }
 }
+impl AstNode for TypeAnchor {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        TYPE_ANCHOR
+    }
+    #[inline]
+    fn can_cast(kind: SyntaxKind) -> bool { kind == TYPE_ANCHOR }
+    #[inline]
+    fn cast(syntax: SyntaxNode) -> Option<Self> {
+        if Self::can_cast(syntax.kind()) {
+            Some(Self { syntax })
+        } else {
+            None
+        }
+    }
+    #[inline]
+    fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl hash::Hash for TypeAnchor {
+    fn hash<H: hash::Hasher>(&self, state: &mut H) { self.syntax.hash(state); }
+}
+impl Eq for TypeAnchor {}
+impl PartialEq for TypeAnchor {
+    fn eq(&self, other: &Self) -> bool { self.syntax == other.syntax }
+}
+impl Clone for TypeAnchor {
+    fn clone(&self) -> Self { Self { syntax: self.syntax.clone() } }
+}
+impl fmt::Debug for TypeAnchor {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.debug_struct("TypeAnchor").field("syntax", &self.syntax).finish()
+    }
+}
 impl AstNode for TypeArg {
     #[inline]
     fn kind() -> SyntaxKind
@@ -10624,6 +10667,11 @@ impl std::fmt::Display for TypeAlias {
         std::fmt::Display::fmt(self.syntax(), f)
     }
 }
+impl std::fmt::Display for TypeAnchor {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        std::fmt::Display::fmt(self.syntax(), f)
+    }
+}
 impl std::fmt::Display for TypeArg {
     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 b9ccd34cff0..dcf853427e5 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
@@ -276,18 +276,15 @@ impl ast::PathSegment {
                 _ => PathSegmentKind::Name(name_ref),
             }
         } else {
-            match self.syntax().first_child_or_token()?.kind() {
-                T![<] => {
-                    // <T> or <T as Trait>
-                    // T is any TypeRef, Trait has to be a PathType
-                    let mut type_refs =
-                        self.syntax().children().filter(|node| ast::Type::can_cast(node.kind()));
-                    let type_ref = type_refs.next().and_then(ast::Type::cast);
-                    let trait_ref = type_refs.next().and_then(ast::PathType::cast);
-                    PathSegmentKind::Type { type_ref, trait_ref }
-                }
-                _ => return None,
-            }
+            let anchor = self.type_anchor()?;
+            // FIXME: Move this over to `ast::TypeAnchor`
+            // <T> or <T as Trait>
+            // T is any TypeRef, Trait has to be a PathType
+            let mut type_refs =
+                anchor.syntax().children().filter(|node| ast::Type::can_cast(node.kind()));
+            let type_ref = type_refs.next().and_then(ast::Type::cast);
+            let trait_ref = type_refs.next().and_then(ast::PathType::cast);
+            PathSegmentKind::Type { type_ref, trait_ref }
         };
         Some(res)
     }
@@ -473,7 +470,7 @@ impl ast::Impl {
 // [#15778](https://github.com/rust-lang/rust-analyzer/issues/15778)
 impl ast::PathSegment {
     pub fn qualifying_trait(&self) -> Option<ast::PathType> {
-        let mut path_types = support::children(self.syntax());
+        let mut path_types = support::children(self.type_anchor()?.syntax());
         let first = path_types.next()?;
         path_types.next().or(Some(first))
     }
diff --git a/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs b/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs
index 96e1301f227..8eb48f8d93e 100644
--- a/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs
@@ -538,6 +538,21 @@ pub fn disallow_cfg(_attr: TokenStream, input: TokenStream) -> TokenStream {
                 disabled: false,
             },
         ),
+        (
+            r#"
+#[proc_macro_attribute]
+pub fn generate_suffixed_type(_attr: TokenStream, input: TokenStream) -> TokenStream {
+    input
+}
+"#
+            .into(),
+            ProcMacro {
+                name: Symbol::intern("generate_suffixed_type"),
+                kind: ProcMacroKind::Attr,
+                expander: sync::Arc::new(GenerateSuffixedTypeProcMacroExpander),
+                disabled: false,
+            },
+        ),
     ])
 }
 
@@ -919,3 +934,57 @@ impl ProcMacroExpander for DisallowCfgProcMacroExpander {
         Ok(subtree.clone())
     }
 }
+
+// Generates a new type by adding a suffix to the original name
+#[derive(Debug)]
+struct GenerateSuffixedTypeProcMacroExpander;
+impl ProcMacroExpander for GenerateSuffixedTypeProcMacroExpander {
+    fn expand(
+        &self,
+        subtree: &TopSubtree,
+        _attrs: Option<&TopSubtree>,
+        _env: &Env,
+        _def_site: Span,
+        call_site: Span,
+        _mixed_site: Span,
+        _current_dir: String,
+    ) -> Result<TopSubtree, ProcMacroExpansionError> {
+        let TokenTree::Leaf(Leaf::Ident(ident)) = &subtree.0[1] else {
+            return Err(ProcMacroExpansionError::Panic("incorrect Input".into()));
+        };
+
+        let ident = match ident.sym.as_str() {
+            "struct" => {
+                let TokenTree::Leaf(Leaf::Ident(ident)) = &subtree.0[2] else {
+                    return Err(ProcMacroExpansionError::Panic("incorrect Input".into()));
+                };
+                ident
+            }
+
+            "enum" => {
+                let TokenTree::Leaf(Leaf::Ident(ident)) = &subtree.0[4] else {
+                    return Err(ProcMacroExpansionError::Panic("incorrect Input".into()));
+                };
+                ident
+            }
+
+            _ => {
+                return Err(ProcMacroExpansionError::Panic("incorrect Input".into()));
+            }
+        };
+
+        let generated_ident = tt::Ident {
+            sym: Symbol::intern(&format!("{}Suffix", ident.sym)),
+            span: ident.span,
+            is_raw: tt::IdentIsRaw::No,
+        };
+
+        let ret = quote! { call_site =>
+            #subtree
+
+            struct #generated_ident;
+        };
+
+        Ok(ret)
+    }
+}
diff --git a/src/tools/rust-analyzer/docs/book/src/non_cargo_based_projects.md b/src/tools/rust-analyzer/docs/book/src/non_cargo_based_projects.md
index 151f8758a17..bbdb48bbbc9 100644
--- a/src/tools/rust-analyzer/docs/book/src/non_cargo_based_projects.md
+++ b/src/tools/rust-analyzer/docs/book/src/non_cargo_based_projects.md
@@ -5,7 +5,7 @@ build system, you’ll have to describe the structure of your project for
 rust-analyzer in the `rust-project.json` format:
 
 ```typescript
-interface JsonProject {
+interface ProjectJson {
     /// Path to the sysroot directory.
     ///
     /// The sysroot is where rustc looks for the
diff --git a/src/tools/rust-analyzer/editors/code/package-lock.json b/src/tools/rust-analyzer/editors/code/package-lock.json
index 18fb097aad7..ab431140c10 100644
--- a/src/tools/rust-analyzer/editors/code/package-lock.json
+++ b/src/tools/rust-analyzer/editors/code/package-lock.json
@@ -5500,9 +5500,9 @@
             }
         },
         "node_modules/tar-fs": {
-            "version": "2.1.2",
-            "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.2.tgz",
-            "integrity": "sha512-EsaAXwxmx8UB7FRKqeozqEPop69DXcmYwTQwXvyAPF352HJsPdkVhvTaDPYqfNgruveJIJy3TA2l+2zj8LJIJA==",
+            "version": "2.1.3",
+            "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.3.tgz",
+            "integrity": "sha512-090nwYJDmlhwFwEW3QQl+vaNnxsO2yVsd45eTKRBzSzu+hlb1w2K9inVq5b0ngXuLVqQ4ApvsUHHnu/zQNkWAg==",
             "dev": true,
             "license": "MIT",
             "optional": true,
diff --git a/src/tools/rust-analyzer/rust-version b/src/tools/rust-analyzer/rust-version
index 5b47d1bbaed..af0dd5c9acd 100644
--- a/src/tools/rust-analyzer/rust-version
+++ b/src/tools/rust-analyzer/rust-version
@@ -1 +1 @@
-a8e4c68dcb4dc1e48a0db294c5323cab0227fcb9
+7c10378e1fee5ddc6573b916aeb884ab10e0de17
diff --git a/src/tools/rustbook/Cargo.lock b/src/tools/rustbook/Cargo.lock
index 8893846b5fa..ed67fa7d1a9 100644
--- a/src/tools/rustbook/Cargo.lock
+++ b/src/tools/rustbook/Cargo.lock
@@ -47,9 +47,9 @@ dependencies = [
 
 [[package]]
 name = "anstream"
-version = "0.6.18"
+version = "0.6.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b"
+checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933"
 dependencies = [
  "anstyle",
  "anstyle-parse",
@@ -62,36 +62,36 @@ dependencies = [
 
 [[package]]
 name = "anstyle"
-version = "1.0.10"
+version = "1.0.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
+checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd"
 
 [[package]]
 name = "anstyle-parse"
-version = "0.2.6"
+version = "0.2.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"
+checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2"
 dependencies = [
  "utf8parse",
 ]
 
 [[package]]
 name = "anstyle-query"
-version = "1.1.2"
+version = "1.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
+checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9"
 dependencies = [
  "windows-sys",
 ]
 
 [[package]]
 name = "anstyle-wincon"
-version = "3.0.7"
+version = "3.0.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e"
+checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882"
 dependencies = [
  "anstyle",
- "once_cell",
+ "once_cell_polyfill",
  "windows-sys",
 ]
 
@@ -124,9 +124,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
 
 [[package]]
 name = "bitflags"
-version = "2.9.0"
+version = "2.9.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"
+checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
 
 [[package]]
 name = "block-buffer"
@@ -150,15 +150,15 @@ dependencies = [
 
 [[package]]
 name = "bumpalo"
-version = "3.17.0"
+version = "3.18.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf"
+checksum = "793db76d6187cd04dff33004d8e6c9cc4e05cd330500379d2394209271b4aeee"
 
 [[package]]
 name = "cc"
-version = "1.2.22"
+version = "1.2.26"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "32db95edf998450acc7881c932f94cd9b05c87b4b2599e8bab064753da4acfd1"
+checksum = "956a5e21988b87f372569b66183b78babf23ebc2e744b733e4350a752c4dafac"
 dependencies = [
  "shlex",
 ]
@@ -185,9 +185,9 @@ dependencies = [
 
 [[package]]
 name = "clap"
-version = "4.5.38"
+version = "4.5.39"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ed93b9805f8ba930df42c2590f05453d5ec36cbb85d018868a5b24d31f6ac000"
+checksum = "fd60e63e9be68e5fb56422e397cf9baddded06dae1d2e523401542383bc72a9f"
 dependencies = [
  "clap_builder",
  "clap_derive",
@@ -195,9 +195,9 @@ dependencies = [
 
 [[package]]
 name = "clap_builder"
-version = "4.5.38"
+version = "4.5.39"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "379026ff283facf611b0ea629334361c4211d1b12ee01024eec1591133b04120"
+checksum = "89cc6392a1f72bbeb820d71f32108f61fdaf18bc526e1d23954168a67759ef51"
 dependencies = [
  "anstream",
  "anstyle",
@@ -208,9 +208,9 @@ dependencies = [
 
 [[package]]
 name = "clap_complete"
-version = "4.5.50"
+version = "4.5.52"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c91d3baa3bcd889d60e6ef28874126a0b384fd225ab83aa6d8a801c519194ce1"
+checksum = "1a554639e42d0c838336fc4fbedb9e2df3ad1fa4acda149f9126b4ccfcd7900f"
 dependencies = [
  "clap",
 ]
@@ -235,9 +235,9 @@ checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
 
 [[package]]
 name = "colorchoice"
-version = "1.0.3"
+version = "1.0.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
+checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
 
 [[package]]
 name = "core-foundation-sys"
@@ -459,9 +459,9 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
 
 [[package]]
 name = "errno"
-version = "0.3.11"
+version = "0.3.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e"
+checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18"
 dependencies = [
  "libc",
  "windows-sys",
@@ -482,9 +482,9 @@ checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
 
 [[package]]
 name = "flate2"
-version = "1.1.1"
+version = "1.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece"
+checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d"
 dependencies = [
  "crc32fast",
  "miniz_oxide",
@@ -531,7 +531,7 @@ version = "0.2.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5"
 dependencies = [
- "unicode-width",
+ "unicode-width 0.1.14",
 ]
 
 [[package]]
@@ -564,9 +564,9 @@ dependencies = [
 
 [[package]]
 name = "hashbrown"
-version = "0.15.3"
+version = "0.15.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3"
+checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5"
 
 [[package]]
 name = "heck"
@@ -680,9 +680,9 @@ checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3"
 
 [[package]]
 name = "icu_properties"
-version = "2.0.0"
+version = "2.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2549ca8c7241c82f59c80ba2a6f415d931c5b58d24fb8412caa1a1f02c49139a"
+checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b"
 dependencies = [
  "displaydoc",
  "icu_collections",
@@ -696,9 +696,9 @@ dependencies = [
 
 [[package]]
 name = "icu_properties_data"
-version = "2.0.0"
+version = "2.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8197e866e47b68f8f7d95249e172903bec06004b18b2937f1095d40a0c57de04"
+checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632"
 
 [[package]]
 name = "icu_provider"
@@ -768,9 +768,9 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
 
 [[package]]
 name = "jiff"
-version = "0.2.13"
+version = "0.2.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f02000660d30638906021176af16b17498bd0d12813dbfe7b276d8bc7f3c0806"
+checksum = "a194df1107f33c79f4f93d02c80798520551949d59dfad22b6157048a88cca93"
 dependencies = [
  "jiff-static",
  "log",
@@ -781,9 +781,9 @@ dependencies = [
 
 [[package]]
 name = "jiff-static"
-version = "0.2.13"
+version = "0.2.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f3c30758ddd7188629c6713fc45d1188af4f44c90582311d0c8d8c9907f60c48"
+checksum = "6c6e1db7ed32c6c71b759497fae34bf7933636f75a251b9e736555da426f6442"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -835,9 +835,9 @@ checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956"
 
 [[package]]
 name = "lock_api"
-version = "0.4.12"
+version = "0.4.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
+checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765"
 dependencies = [
  "autocfg",
  "scopeguard",
@@ -961,7 +961,7 @@ dependencies = [
  "pulldown-cmark-to-cmark 19.0.1",
  "serde_json",
  "thiserror 1.0.69",
- "toml 0.8.22",
+ "toml 0.8.23",
 ]
 
 [[package]]
@@ -1025,12 +1025,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
 
 [[package]]
+name = "once_cell_polyfill"
+version = "1.70.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad"
+
+[[package]]
 name = "onig"
 version = "6.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "336b9c63443aceef14bea841b899035ae3abe89b7c486aaf4c5bd8aafedac3f0"
 dependencies = [
- "bitflags 2.9.0",
+ "bitflags 2.9.1",
  "libc",
  "once_cell",
  "onig_sys",
@@ -1048,9 +1054,9 @@ dependencies = [
 
 [[package]]
 name = "opener"
-version = "0.8.1"
+version = "0.8.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "de96cad6ee771be7f68df884d3767460b4684012308d8342ed5623fe62b2628c"
+checksum = "771b9704f8cd8b424ec747a320b30b47517a6966ba2c7da90047c16f4a962223"
 dependencies = [
  "bstr",
  "normpath",
@@ -1059,9 +1065,9 @@ dependencies = [
 
 [[package]]
 name = "parking_lot"
-version = "0.12.3"
+version = "0.12.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27"
+checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13"
 dependencies = [
  "lock_api",
  "parking_lot_core",
@@ -1069,9 +1075,9 @@ dependencies = [
 
 [[package]]
 name = "parking_lot_core"
-version = "0.9.10"
+version = "0.9.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
+checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5"
 dependencies = [
  "cfg-if",
  "libc",
@@ -1206,9 +1212,9 @@ dependencies = [
 
 [[package]]
 name = "portable-atomic"
-version = "1.11.0"
+version = "1.11.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e"
+checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483"
 
 [[package]]
 name = "portable-atomic-util"
@@ -1249,7 +1255,7 @@ version = "0.10.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "76979bea66e7875e7509c4ec5300112b316af87fa7a252ca91c448b32dfe3993"
 dependencies = [
- "bitflags 2.9.0",
+ "bitflags 2.9.1",
  "memchr",
  "pulldown-cmark-escape 0.10.1",
  "unicase",
@@ -1261,7 +1267,7 @@ version = "0.12.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f86ba2052aebccc42cbbb3ed234b8b13ce76f75c3551a303cb2bcffcff12bb14"
 dependencies = [
- "bitflags 2.9.0",
+ "bitflags 2.9.1",
  "getopts",
  "memchr",
  "pulldown-cmark-escape 0.11.0",
@@ -1315,11 +1321,11 @@ checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5"
 
 [[package]]
 name = "railroad"
-version = "0.3.2"
+version = "0.3.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0ecedffc46c1b2cb04f4b80e094eae6b3f3f470a9635f1f396dd5206428f6b58"
+checksum = "e6d5b8e8a7c20c600f9b98cbf46b64e63d5c9e69deb98cee1ff264de9f1dda5d"
 dependencies = [
- "unicode-width",
+ "unicode-width 0.2.0",
 ]
 
 [[package]]
@@ -1343,7 +1349,7 @@ version = "0.5.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af"
 dependencies = [
- "bitflags 2.9.0",
+ "bitflags 2.9.1",
 ]
 
 [[package]]
@@ -1393,7 +1399,7 @@ version = "1.0.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266"
 dependencies = [
- "bitflags 2.9.0",
+ "bitflags 2.9.1",
  "errno",
  "libc",
  "linux-raw-sys",
@@ -1402,9 +1408,9 @@ dependencies = [
 
 [[package]]
 name = "rustversion"
-version = "1.0.20"
+version = "1.0.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2"
+checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d"
 
 [[package]]
 name = "ryu"
@@ -1467,9 +1473,9 @@ dependencies = [
 
 [[package]]
 name = "serde_spanned"
-version = "0.6.8"
+version = "0.6.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1"
+checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3"
 dependencies = [
  "serde",
 ]
@@ -1499,9 +1505,9 @@ checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d"
 
 [[package]]
 name = "smallvec"
-version = "1.15.0"
+version = "1.15.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9"
+checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
 
 [[package]]
 name = "stable_deref_trait"
@@ -1584,9 +1590,9 @@ dependencies = [
 
 [[package]]
 name = "tempfile"
-version = "3.19.1"
+version = "3.20.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf"
+checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1"
 dependencies = [
  "fastrand",
  "getrandom",
@@ -1683,9 +1689,9 @@ dependencies = [
 
 [[package]]
 name = "toml"
-version = "0.8.22"
+version = "0.8.23"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "05ae329d1f08c4d17a59bed7ff5b5a769d062e64a62d34a3261b219e62cd5aae"
+checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362"
 dependencies = [
  "serde",
  "serde_spanned",
@@ -1695,18 +1701,18 @@ dependencies = [
 
 [[package]]
 name = "toml_datetime"
-version = "0.6.9"
+version = "0.6.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3"
+checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c"
 dependencies = [
  "serde",
 ]
 
 [[package]]
 name = "toml_edit"
-version = "0.22.26"
+version = "0.22.27"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e"
+checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a"
 dependencies = [
  "indexmap",
  "serde",
@@ -1718,9 +1724,9 @@ dependencies = [
 
 [[package]]
 name = "toml_write"
-version = "0.1.1"
+version = "0.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bfb942dfe1d8e29a7ee7fcbde5bd2b9a25fb89aa70caea2eba3bee836ff41076"
+checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801"
 
 [[package]]
 name = "topological-sort"
@@ -1759,6 +1765,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
 
 [[package]]
+name = "unicode-width"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd"
+
+[[package]]
 name = "url"
 version = "2.5.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1872,9 +1884,9 @@ dependencies = [
 
 [[package]]
 name = "web_atoms"
-version = "0.1.2"
+version = "0.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b9c5f0bc545ea3b20b423e33b9b457764de0b3730cd957f6c6aa6c301785f6e"
+checksum = "57ffde1dc01240bdf9992e3205668b235e59421fd085e8a317ed98da0178d414"
 dependencies = [
  "phf",
  "phf_codegen",
@@ -1893,9 +1905,9 @@ dependencies = [
 
 [[package]]
 name = "windows-core"
-version = "0.61.0"
+version = "0.61.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980"
+checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3"
 dependencies = [
  "windows-implement",
  "windows-interface",
@@ -1934,18 +1946,18 @@ checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38"
 
 [[package]]
 name = "windows-result"
-version = "0.3.2"
+version = "0.3.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252"
+checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6"
 dependencies = [
  "windows-link",
 ]
 
 [[package]]
 name = "windows-strings"
-version = "0.4.0"
+version = "0.4.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97"
+checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57"
 dependencies = [
  "windows-link",
 ]
@@ -2038,7 +2050,7 @@ version = "0.39.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
 dependencies = [
- "bitflags 2.9.0",
+ "bitflags 2.9.1",
 ]
 
 [[package]]
diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs
index fdca7a7a40e..170dcd626a2 100644
--- a/src/tools/tidy/src/deps.rs
+++ b/src/tools/tidy/src/deps.rs
@@ -441,6 +441,7 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
     "windows-strings",
     "windows-sys",
     "windows-targets",
+    "windows-threading",
     "windows_aarch64_gnullvm",
     "windows_aarch64_msvc",
     "windows_i686_gnu",