about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_hir_analysis/src/collect/generics_of.rs37
-rw-r--r--compiler/rustc_hir_analysis/src/collect/type_of.rs39
-rw-r--r--compiler/rustc_lint/src/context/diagnostics.rs6
-rw-r--r--compiler/rustc_middle/src/query/mod.rs4
-rw-r--r--compiler/rustc_mir_build/src/build/expr/into.rs81
-rw-r--r--compiler/rustc_resolve/src/check_unused.rs146
-rw-r--r--compiler/rustc_resolve/src/imports.rs2
-rw-r--r--compiler/rustc_target/src/target_features.rs2
-rw-r--r--compiler/rustc_trait_selection/src/solve/trait_goals.rs1
-rw-r--r--compiler/rustc_ty_utils/src/assoc.rs47
-rw-r--r--config.example.toml4
-rw-r--r--library/core/src/array/mod.rs1
-rw-r--r--library/std/src/thread/tests.rs1
-rw-r--r--src/bootstrap/bootstrap.py4
-rwxr-xr-xsrc/bootstrap/configure.py1
-rw-r--r--src/bootstrap/src/core/build_steps/format.rs2
-rw-r--r--src/bootstrap/src/core/config/config.rs4
-rw-r--r--src/bootstrap/src/core/download.rs8
-rw-r--r--src/bootstrap/src/utils/change_tracker.rs5
-rw-r--r--src/tools/build_helper/src/git.rs17
-rw-r--r--tests/ui/abi/compatibility.rs2
-rw-r--r--tests/ui/check-cfg/mix.stderr2
-rw-r--r--tests/ui/check-cfg/well-known-values.stderr2
-rw-r--r--tests/ui/impl-trait/in-trait/rpitit-cycle-in-generics-of.rs18
-rw-r--r--tests/ui/imports/auxiliary/aux-issue-121915.rs1
-rw-r--r--tests/ui/imports/redundant-import-issue-121915-2015.rs11
-rw-r--r--tests/ui/imports/redundant-import-issue-121915-2015.stderr17
-rw-r--r--tests/ui/imports/redundant-import-issue-121915.rs9
-rw-r--r--tests/ui/imports/redundant-import-issue-121915.stderr14
-rw-r--r--tests/ui/imports/suggest-remove-issue-121315.rs40
-rw-r--r--tests/ui/imports/suggest-remove-issue-121315.stderr56
31 files changed, 403 insertions, 181 deletions
diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs
index 9cc6c16c126..c86788db988 100644
--- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs
@@ -14,6 +14,43 @@ use rustc_span::Span;
 pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
     use rustc_hir::*;
 
+    // For an RPITIT, synthesize generics which are equal to the opaque's generics
+    // and parent fn's generics compressed into one list.
+    if let Some(ty::ImplTraitInTraitData::Trait { fn_def_id, opaque_def_id }) =
+        tcx.opt_rpitit_info(def_id.to_def_id())
+    {
+        let trait_def_id = tcx.parent(fn_def_id);
+        let opaque_ty_generics = tcx.generics_of(opaque_def_id);
+        let opaque_ty_parent_count = opaque_ty_generics.parent_count;
+        let mut params = opaque_ty_generics.params.clone();
+
+        let parent_generics = tcx.generics_of(trait_def_id);
+        let parent_count = parent_generics.parent_count + parent_generics.params.len();
+
+        let mut trait_fn_params = tcx.generics_of(fn_def_id).params.clone();
+
+        for param in &mut params {
+            param.index = param.index + parent_count as u32 + trait_fn_params.len() as u32
+                - opaque_ty_parent_count as u32;
+        }
+
+        trait_fn_params.extend(params);
+        params = trait_fn_params;
+
+        let param_def_id_to_index =
+            params.iter().map(|param| (param.def_id, param.index)).collect();
+
+        return ty::Generics {
+            parent: Some(trait_def_id),
+            parent_count,
+            params,
+            param_def_id_to_index,
+            has_self: opaque_ty_generics.has_self,
+            has_late_bound_regions: opaque_ty_generics.has_late_bound_regions,
+            host_effect_index: parent_generics.host_effect_index,
+        };
+    }
+
     let hir_id = tcx.local_def_id_to_hir_id(def_id);
 
     let node = tcx.hir_node(hir_id);
diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs
index 417f0fceaa8..2217e5280a7 100644
--- a/compiler/rustc_hir_analysis/src/collect/type_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs
@@ -5,7 +5,7 @@ use rustc_hir::HirId;
 use rustc_middle::query::plumbing::CyclePlaceholder;
 use rustc_middle::ty::print::with_forced_trimmed_paths;
 use rustc_middle::ty::util::IntTypeExt;
-use rustc_middle::ty::{self, ImplTraitInTraitData, IsSuggestable, Ty, TyCtxt, TypeVisitableExt};
+use rustc_middle::ty::{self, IsSuggestable, Ty, TyCtxt, TypeVisitableExt};
 use rustc_span::symbol::Ident;
 use rustc_span::{Span, DUMMY_SP};
 
@@ -350,22 +350,31 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<Ty
     // If we are computing `type_of` the synthesized associated type for an RPITIT in the impl
     // side, use `collect_return_position_impl_trait_in_trait_tys` to infer the value of the
     // associated type in the impl.
-    if let Some(ImplTraitInTraitData::Impl { fn_def_id, .. }) =
-        tcx.opt_rpitit_info(def_id.to_def_id())
-    {
-        match tcx.collect_return_position_impl_trait_in_trait_tys(fn_def_id) {
-            Ok(map) => {
-                let assoc_item = tcx.associated_item(def_id);
-                return map[&assoc_item.trait_item_def_id.unwrap()];
-            }
-            Err(_) => {
-                return ty::EarlyBinder::bind(Ty::new_error_with_message(
-                    tcx,
-                    DUMMY_SP,
-                    "Could not collect return position impl trait in trait tys",
-                ));
+    match tcx.opt_rpitit_info(def_id.to_def_id()) {
+        Some(ty::ImplTraitInTraitData::Impl { fn_def_id }) => {
+            match tcx.collect_return_position_impl_trait_in_trait_tys(fn_def_id) {
+                Ok(map) => {
+                    let assoc_item = tcx.associated_item(def_id);
+                    return map[&assoc_item.trait_item_def_id.unwrap()];
+                }
+                Err(_) => {
+                    return ty::EarlyBinder::bind(Ty::new_error_with_message(
+                        tcx,
+                        DUMMY_SP,
+                        "Could not collect return position impl trait in trait tys",
+                    ));
+                }
             }
         }
+        // For an RPITIT in a trait, just return the corresponding opaque.
+        Some(ty::ImplTraitInTraitData::Trait { opaque_def_id, .. }) => {
+            return ty::EarlyBinder::bind(Ty::new_opaque(
+                tcx,
+                opaque_def_id,
+                ty::GenericArgs::identity_for_item(tcx, opaque_def_id),
+            ));
+        }
+        None => {}
     }
 
     let hir_id = tcx.local_def_id_to_hir_id(def_id);
diff --git a/compiler/rustc_lint/src/context/diagnostics.rs b/compiler/rustc_lint/src/context/diagnostics.rs
index a58a37bf3ac..a0be1c09c9a 100644
--- a/compiler/rustc_lint/src/context/diagnostics.rs
+++ b/compiler/rustc_lint/src/context/diagnostics.rs
@@ -143,7 +143,11 @@ pub(super) fn builtin(sess: &Session, diagnostic: BuiltinLintDiag, diag: &mut Di
         BuiltinLintDiag::RedundantImport(spans, ident) => {
             for (span, is_imported) in spans {
                 let introduced = if is_imported { "imported" } else { "defined" };
-                diag.span_label(span, format!("the item `{ident}` is already {introduced} here"));
+                let span_msg = if span.is_dummy() { "by prelude" } else { "here" };
+                diag.span_label(
+                    span,
+                    format!("the item `{ident}` is already {introduced} {span_msg}"),
+                );
             }
         }
         BuiltinLintDiag::DeprecatedMacro(suggestion, span) => {
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index b0431ae05d3..ebfdf8aa197 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -826,7 +826,7 @@ rustc_queries! {
     /// creates and returns the associated items that correspond to each impl trait in return position
     /// of the implemented trait.
     query associated_types_for_impl_traits_in_associated_fn(fn_def_id: DefId) -> &'tcx [DefId] {
-        desc { |tcx| "creating associated items for impl trait in trait returned by `{}`", tcx.def_path_str(fn_def_id) }
+        desc { |tcx| "creating associated items for opaque types returned by `{}`", tcx.def_path_str(fn_def_id) }
         cache_on_disk_if { fn_def_id.is_local() }
         separate_provide_extern
     }
@@ -834,7 +834,7 @@ rustc_queries! {
     /// Given an impl trait in trait `opaque_ty_def_id`, create and return the corresponding
     /// associated item.
     query associated_type_for_impl_trait_in_trait(opaque_ty_def_id: LocalDefId) -> LocalDefId {
-        desc { |tcx| "creates the associated item corresponding to the opaque type `{}`", tcx.def_path_str(opaque_ty_def_id.to_def_id()) }
+        desc { |tcx| "creating the associated item corresponding to the opaque type `{}`", tcx.def_path_str(opaque_ty_def_id.to_def_id()) }
         cache_on_disk_if { true }
     }
 
diff --git a/compiler/rustc_mir_build/src/build/expr/into.rs b/compiler/rustc_mir_build/src/build/expr/into.rs
index 9a6d4498352..69f3d3101fa 100644
--- a/compiler/rustc_mir_build/src/build/expr/into.rs
+++ b/compiler/rustc_mir_build/src/build/expr/into.rs
@@ -58,52 +58,61 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                 this.thir[scrutinee].span,
             ),
             ExprKind::If { cond, then, else_opt, if_then_scope } => {
-                let then_blk;
                 let then_span = this.thir[then].span;
                 let then_source_info = this.source_info(then_span);
                 let condition_scope = this.local_scope();
 
-                let mut else_blk = unpack!(
-                    then_blk = this.in_scope(
-                        (if_then_scope, then_source_info),
-                        LintLevel::Inherited,
-                        |this| {
-                            let source_info = if this.is_let(cond) {
-                                let variable_scope =
-                                    this.new_source_scope(then_span, LintLevel::Inherited, None);
-                                this.source_scope = variable_scope;
-                                SourceInfo { span: then_span, scope: variable_scope }
-                            } else {
-                                this.source_info(then_span)
-                            };
-                            let (then_block, else_block) =
-                                this.in_if_then_scope(condition_scope, then_span, |this| {
-                                    let then_blk = unpack!(this.then_else_break(
-                                        block,
-                                        cond,
-                                        Some(condition_scope), // Temp scope
-                                        condition_scope,
-                                        source_info,
-                                        true, // Declare `let` bindings normally
-                                    ));
-
-                                    this.expr_into_dest(destination, then_blk, then)
-                                });
-                            then_block.and(else_block)
-                        },
-                    )
+                let then_and_else_blocks = this.in_scope(
+                    (if_then_scope, then_source_info),
+                    LintLevel::Inherited,
+                    |this| {
+                        // FIXME: Does this need extra logic to handle let-chains?
+                        let source_info = if this.is_let(cond) {
+                            let variable_scope =
+                                this.new_source_scope(then_span, LintLevel::Inherited, None);
+                            this.source_scope = variable_scope;
+                            SourceInfo { span: then_span, scope: variable_scope }
+                        } else {
+                            this.source_info(then_span)
+                        };
+
+                        // Lower the condition, and have it branch into `then` and `else` blocks.
+                        let (then_block, else_block) =
+                            this.in_if_then_scope(condition_scope, then_span, |this| {
+                                let then_blk = unpack!(this.then_else_break(
+                                    block,
+                                    cond,
+                                    Some(condition_scope), // Temp scope
+                                    condition_scope,
+                                    source_info,
+                                    true, // Declare `let` bindings normally
+                                ));
+
+                                // Lower the `then` arm into its block.
+                                this.expr_into_dest(destination, then_blk, then)
+                            });
+
+                        // Pack `(then_block, else_block)` into `BlockAnd<BasicBlock>`.
+                        then_block.and(else_block)
+                    },
                 );
 
-                else_blk = if let Some(else_opt) = else_opt {
-                    unpack!(this.expr_into_dest(destination, else_blk, else_opt))
+                // Unpack `BlockAnd<BasicBlock>` into `(then_blk, else_blk)`.
+                let (then_blk, mut else_blk);
+                else_blk = unpack!(then_blk = then_and_else_blocks);
+
+                // If there is an `else` arm, lower it into `else_blk`.
+                if let Some(else_expr) = else_opt {
+                    unpack!(else_blk = this.expr_into_dest(destination, else_blk, else_expr));
                 } else {
-                    // Body of the `if` expression without an `else` clause must return `()`, thus
-                    // we implicitly generate an `else {}` if it is not specified.
+                    // There is no `else` arm, so we know both arms have type `()`.
+                    // Generate the implicit `else {}` by assigning unit.
                     let correct_si = this.source_info(expr_span.shrink_to_hi());
                     this.cfg.push_assign_unit(else_blk, correct_si, destination, this.tcx);
-                    else_blk
-                };
+                }
 
+                // The `then` and `else` arms have been lowered into their respective
+                // blocks, so make both of them meet up in a new block.
                 let join_block = this.cfg.start_new_block();
                 this.cfg.goto(then_blk, source_info, join_block);
                 this.cfg.goto(else_blk, source_info, join_block);
diff --git a/compiler/rustc_resolve/src/check_unused.rs b/compiler/rustc_resolve/src/check_unused.rs
index 13fec70e0a7..bf1ea2e2709 100644
--- a/compiler/rustc_resolve/src/check_unused.rs
+++ b/compiler/rustc_resolve/src/check_unused.rs
@@ -137,6 +137,81 @@ impl<'a, 'b, 'tcx> UnusedImportCheckVisitor<'a, 'b, 'tcx> {
             self.check_import_as_underscore(item, *id);
         }
     }
+
+    fn report_unused_extern_crate_items(
+        &mut self,
+        maybe_unused_extern_crates: FxHashMap<ast::NodeId, Span>,
+    ) {
+        let tcx = self.r.tcx();
+        for extern_crate in &self.extern_crate_items {
+            let warn_if_unused = !extern_crate.ident.name.as_str().starts_with('_');
+
+            // If the crate is fully unused, we suggest removing it altogether.
+            // We do this in any edition.
+            if warn_if_unused {
+                if let Some(&span) = maybe_unused_extern_crates.get(&extern_crate.id) {
+                    self.r.lint_buffer.buffer_lint_with_diagnostic(
+                        UNUSED_EXTERN_CRATES,
+                        extern_crate.id,
+                        span,
+                        "unused extern crate",
+                        BuiltinLintDiag::UnusedExternCrate {
+                            removal_span: extern_crate.span_with_attributes,
+                        },
+                    );
+                    continue;
+                }
+            }
+
+            // If we are not in Rust 2018 edition, then we don't make any further
+            // suggestions.
+            if !tcx.sess.at_least_rust_2018() {
+                continue;
+            }
+
+            // If the extern crate has any attributes, they may have funky
+            // semantics we can't faithfully represent using `use` (most
+            // notably `#[macro_use]`). Ignore it.
+            if extern_crate.has_attrs {
+                continue;
+            }
+
+            // If the extern crate is renamed, then we cannot suggest replacing it with a use as this
+            // would not insert the new name into the prelude, where other imports in the crate may be
+            // expecting it.
+            if extern_crate.renames {
+                continue;
+            }
+
+            // If the extern crate isn't in the extern prelude,
+            // there is no way it can be written as a `use`.
+            if !self
+                .r
+                .extern_prelude
+                .get(&extern_crate.ident)
+                .is_some_and(|entry| !entry.introduced_by_item)
+            {
+                continue;
+            }
+
+            let vis_span = extern_crate
+                .vis_span
+                .find_ancestor_inside(extern_crate.span)
+                .unwrap_or(extern_crate.vis_span);
+            let ident_span = extern_crate
+                .ident
+                .span
+                .find_ancestor_inside(extern_crate.span)
+                .unwrap_or(extern_crate.ident.span);
+            self.r.lint_buffer.buffer_lint_with_diagnostic(
+                UNUSED_EXTERN_CRATES,
+                extern_crate.id,
+                extern_crate.span,
+                "`extern crate` is not idiomatic in the new edition",
+                BuiltinLintDiag::ExternCrateNotIdiomatic { vis_span, ident_span },
+            );
+        }
+    }
 }
 
 impl<'a, 'b, 'tcx> Visitor<'a> for UnusedImportCheckVisitor<'a, 'b, 'tcx> {
@@ -335,6 +410,8 @@ impl Resolver<'_, '_> {
         };
         visit::walk_crate(&mut visitor, krate);
 
+        visitor.report_unused_extern_crate_items(maybe_unused_extern_crates);
+
         for unused in visitor.unused_imports.values() {
             let mut fixes = Vec::new();
             let spans = match calc_unused_spans(unused, &unused.use_tree, unused.use_tree_id) {
@@ -416,75 +493,6 @@ impl Resolver<'_, '_> {
             );
         }
 
-        for extern_crate in visitor.extern_crate_items {
-            let warn_if_unused = !extern_crate.ident.name.as_str().starts_with('_');
-
-            // If the crate is fully unused, we suggest removing it altogether.
-            // We do this in any edition.
-            if warn_if_unused {
-                if let Some(&span) = maybe_unused_extern_crates.get(&extern_crate.id) {
-                    visitor.r.lint_buffer.buffer_lint_with_diagnostic(
-                        UNUSED_EXTERN_CRATES,
-                        extern_crate.id,
-                        span,
-                        "unused extern crate",
-                        BuiltinLintDiag::UnusedExternCrate {
-                            removal_span: extern_crate.span_with_attributes,
-                        },
-                    );
-                    continue;
-                }
-            }
-
-            // If we are not in Rust 2018 edition, then we don't make any further
-            // suggestions.
-            if !tcx.sess.at_least_rust_2018() {
-                continue;
-            }
-
-            // If the extern crate has any attributes, they may have funky
-            // semantics we can't faithfully represent using `use` (most
-            // notably `#[macro_use]`). Ignore it.
-            if extern_crate.has_attrs {
-                continue;
-            }
-
-            // If the extern crate is renamed, then we cannot suggest replacing it with a use as this
-            // would not insert the new name into the prelude, where other imports in the crate may be
-            // expecting it.
-            if extern_crate.renames {
-                continue;
-            }
-
-            // If the extern crate isn't in the extern prelude,
-            // there is no way it can be written as a `use`.
-            if !visitor
-                .r
-                .extern_prelude
-                .get(&extern_crate.ident)
-                .is_some_and(|entry| !entry.introduced_by_item)
-            {
-                continue;
-            }
-
-            let vis_span = extern_crate
-                .vis_span
-                .find_ancestor_inside(extern_crate.span)
-                .unwrap_or(extern_crate.vis_span);
-            let ident_span = extern_crate
-                .ident
-                .span
-                .find_ancestor_inside(extern_crate.span)
-                .unwrap_or(extern_crate.ident.span);
-            visitor.r.lint_buffer.buffer_lint_with_diagnostic(
-                UNUSED_EXTERN_CRATES,
-                extern_crate.id,
-                extern_crate.span,
-                "`extern crate` is not idiomatic in the new edition",
-                BuiltinLintDiag::ExternCrateNotIdiomatic { vis_span, ident_span },
-            );
-        }
-
         let unused_imports = visitor.unused_imports;
         let mut check_redundant_imports = FxIndexSet::default();
         for module in self.arenas.local_modules().iter() {
diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs
index 9e5b2fe094f..ea08041f2aa 100644
--- a/compiler/rustc_resolve/src/imports.rs
+++ b/compiler/rustc_resolve/src/imports.rs
@@ -1336,9 +1336,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
         }
 
         let mut is_redundant = true;
-
         let mut redundant_span = PerNS { value_ns: None, type_ns: None, macro_ns: None };
-
         self.per_ns(|this, ns| {
             if is_redundant && let Ok(binding) = source_bindings[ns].get() {
                 if binding.res() == Res::Err {
diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs
index 2b7ac68c21d..b5cee4f34f5 100644
--- a/compiler/rustc_target/src/target_features.rs
+++ b/compiler/rustc_target/src/target_features.rs
@@ -377,10 +377,12 @@ const LOONGARCH_ALLOWED_FEATURES: &[(&str, Stability)] = &[
     // tidy-alphabetical-start
     ("d", Unstable(sym::loongarch_target_feature)),
     ("f", Unstable(sym::loongarch_target_feature)),
+    ("frecipe", Unstable(sym::loongarch_target_feature)),
     ("lasx", Unstable(sym::loongarch_target_feature)),
     ("lbt", Unstable(sym::loongarch_target_feature)),
     ("lsx", Unstable(sym::loongarch_target_feature)),
     ("lvz", Unstable(sym::loongarch_target_feature)),
+    ("relax", Unstable(sym::loongarch_target_feature)),
     ("ual", Unstable(sym::loongarch_target_feature)),
     // tidy-alphabetical-end
 ];
diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs
index 80198ba39f9..16febf05b60 100644
--- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs
+++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs
@@ -102,7 +102,6 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
             if trait_clause.def_id() == goal.predicate.def_id()
                 && trait_clause.polarity() == goal.predicate.polarity
             {
-                // FIXME: Constness
                 ecx.probe_misc_candidate("assumption").enter(|ecx| {
                     let assumption_trait_pred = ecx.instantiate_binder_with_infer(trait_clause);
                     ecx.eq(
diff --git a/compiler/rustc_ty_utils/src/assoc.rs b/compiler/rustc_ty_utils/src/assoc.rs
index db37bec4b82..26d3370469a 100644
--- a/compiler/rustc_ty_utils/src/assoc.rs
+++ b/compiler/rustc_ty_utils/src/assoc.rs
@@ -4,7 +4,7 @@ use rustc_hir::def::DefKind;
 use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId};
 use rustc_hir::intravisit::{self, Visitor};
 use rustc_middle::query::Providers;
-use rustc_middle::ty::{self, GenericArgs, ImplTraitInTraitData, Ty, TyCtxt};
+use rustc_middle::ty::{self, ImplTraitInTraitData, TyCtxt};
 use rustc_span::symbol::kw;
 
 pub(crate) fn provide(providers: &mut Providers) {
@@ -284,48 +284,8 @@ fn associated_type_for_impl_trait_in_trait(
     // Copy defaultness of the containing function.
     trait_assoc_ty.defaultness(tcx.defaultness(fn_def_id));
 
-    // Copy type_of of the opaque.
-    trait_assoc_ty.type_of(ty::EarlyBinder::bind(Ty::new_opaque(
-        tcx,
-        opaque_ty_def_id.to_def_id(),
-        GenericArgs::identity_for_item(tcx, opaque_ty_def_id),
-    )));
-
     trait_assoc_ty.is_type_alias_impl_trait(false);
 
-    // Copy generics_of of the opaque type item but the trait is the parent.
-    trait_assoc_ty.generics_of({
-        let opaque_ty_generics = tcx.generics_of(opaque_ty_def_id);
-        let opaque_ty_parent_count = opaque_ty_generics.parent_count;
-        let mut params = opaque_ty_generics.params.clone();
-
-        let parent_generics = tcx.generics_of(trait_def_id);
-        let parent_count = parent_generics.parent_count + parent_generics.params.len();
-
-        let mut trait_fn_params = tcx.generics_of(fn_def_id).params.clone();
-
-        for param in &mut params {
-            param.index = param.index + parent_count as u32 + trait_fn_params.len() as u32
-                - opaque_ty_parent_count as u32;
-        }
-
-        trait_fn_params.extend(params);
-        params = trait_fn_params;
-
-        let param_def_id_to_index =
-            params.iter().map(|param| (param.def_id, param.index)).collect();
-
-        ty::Generics {
-            parent: Some(trait_def_id.to_def_id()),
-            parent_count,
-            params,
-            param_def_id_to_index,
-            has_self: opaque_ty_generics.has_self,
-            has_late_bound_regions: opaque_ty_generics.has_late_bound_regions,
-            host_effect_index: parent_generics.host_effect_index,
-        }
-    });
-
     // There are no inferred outlives for the synthesized associated type.
     trait_assoc_ty.inferred_outlives_of(&[]);
 
@@ -382,8 +342,9 @@ fn associated_type_for_impl_trait_in_impl(
     impl_assoc_ty.defaultness(tcx.defaultness(impl_fn_def_id));
 
     // Copy generics_of the trait's associated item but the impl as the parent.
-    // FIXME(-Zlower-impl-trait-in-trait-to-assoc-ty) resolves to the trait instead of the impl
-    // generics.
+    // FIXME: This may be detrimental to diagnostics, as we resolve the early-bound vars
+    // here to paramswhose parent are items in the trait. We could synthesize new params
+    // here, but it seems overkill.
     impl_assoc_ty.generics_of({
         let trait_assoc_generics = tcx.generics_of(trait_assoc_def_id);
         let trait_assoc_parent_count = trait_assoc_generics.parent_count;
diff --git a/config.example.toml b/config.example.toml
index d12ed052fe4..c1939933850 100644
--- a/config.example.toml
+++ b/config.example.toml
@@ -300,6 +300,10 @@
 # This is only useful for verifying that rustc generates reproducible builds.
 #full-bootstrap = false
 
+# Set the bootstrap/download cache path. It is useful when building rust
+# repeatedly in a CI invironment.
+# bootstrap-cache-path = /shared/cache
+
 # Enable a build of the extended Rust tool set which is not only the compiler
 # but also tools such as Cargo. This will also produce "combined installers"
 # which are used to install Rust and Cargo together.
diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs
index 42663ff2b53..8b5b48c59c2 100644
--- a/library/core/src/array/mod.rs
+++ b/library/core/src/array/mod.rs
@@ -360,6 +360,7 @@ where
     }
 }
 
+/// Implements comparison of arrays [lexicographically](Ord#lexicographical-comparison).
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<T: PartialOrd, const N: usize> PartialOrd for [T; N] {
     #[inline]
diff --git a/library/std/src/thread/tests.rs b/library/std/src/thread/tests.rs
index b81efac6761..589a5fdad1d 100644
--- a/library/std/src/thread/tests.rs
+++ b/library/std/src/thread/tests.rs
@@ -80,6 +80,7 @@ fn test_named_thread_truncation() {
 #[test]
 fn test_get_os_named_thread() {
     use crate::sys::thread::Thread;
+    // Spawn a new thread to avoid interfering with other tests running on this thread.
     let handler = thread::spawn(|| {
         let name = c"test me please";
         Thread::set_name(name);
diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py
index 0d604c0d3e5..6e49bcc9744 100644
--- a/src/bootstrap/bootstrap.py
+++ b/src/bootstrap/bootstrap.py
@@ -557,7 +557,9 @@ class RustBuild(object):
                 shutil.rmtree(bin_root)
 
             key = self.stage0_compiler.date
-            cache_dst = os.path.join(self.build_dir, "cache")
+            cache_dst = (self.get_toml('bootstrap-cache-path', 'build') or
+                os.path.join(self.build_dir, "cache"))
+
             rustc_cache = os.path.join(cache_dst, key)
             if not os.path.exists(rustc_cache):
                 os.makedirs(rustc_cache)
diff --git a/src/bootstrap/configure.py b/src/bootstrap/configure.py
index 8b65e8ff9c3..4257c0f7991 100755
--- a/src/bootstrap/configure.py
+++ b/src/bootstrap/configure.py
@@ -149,6 +149,7 @@ v("default-linker", "rust.default-linker", "the default linker")
 # (others are conditionally saved).
 o("manage-submodules", "build.submodules", "let the build manage the git submodules")
 o("full-bootstrap", "build.full-bootstrap", "build three compilers instead of two (not recommended except for testing reproducible builds)")
+o("bootstrap-cache-path", "build.bootstrap-cache-path", "use provided path for the bootstrap cache")
 o("extended", "build.extended", "build an extended rust tool set")
 
 v("tools", None, "List of extended tools will be installed")
diff --git a/src/bootstrap/src/core/build_steps/format.rs b/src/bootstrap/src/core/build_steps/format.rs
index 700c3ee4fda..fc9f9789bd6 100644
--- a/src/bootstrap/src/core/build_steps/format.rs
+++ b/src/bootstrap/src/core/build_steps/format.rs
@@ -81,7 +81,7 @@ fn update_rustfmt_version(build: &Builder<'_>) {
 }
 
 /// Returns the Rust files modified between the `merge-base` of HEAD and
-/// rust-lang/master and what is now on the disk.
+/// rust-lang/master and what is now on the disk. Does not include removed files.
 ///
 /// Returns `None` if all files should be formatted.
 fn get_modified_rs_files(build: &Builder<'_>) -> Result<Option<Vec<String>>, String> {
diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs
index 875a4efae02..326f8f57173 100644
--- a/src/bootstrap/src/core/config/config.rs
+++ b/src/bootstrap/src/core/config/config.rs
@@ -161,6 +161,7 @@ pub struct Config {
     pub vendor: bool,
     pub target_config: HashMap<TargetSelection, Target>,
     pub full_bootstrap: bool,
+    pub bootstrap_cache_path: Option<PathBuf>,
     pub extended: bool,
     pub tools: Option<HashSet<String>>,
     pub sanitizers: bool,
@@ -827,6 +828,7 @@ define_config! {
         locked_deps: Option<bool> = "locked-deps",
         vendor: Option<bool> = "vendor",
         full_bootstrap: Option<bool> = "full-bootstrap",
+        bootstrap_cache_path: Option<PathBuf> = "bootstrap-cache-path",
         extended: Option<bool> = "extended",
         tools: Option<HashSet<String>> = "tools",
         verbose: Option<usize> = "verbose",
@@ -1389,6 +1391,7 @@ impl Config {
             locked_deps,
             vendor,
             full_bootstrap,
+            bootstrap_cache_path,
             extended,
             tools,
             verbose,
@@ -1477,6 +1480,7 @@ impl Config {
         config.reuse = reuse.map(PathBuf::from);
         config.submodules = submodules;
         config.android_ndk = android_ndk;
+        config.bootstrap_cache_path = bootstrap_cache_path;
         set(&mut config.low_priority, low_priority);
         set(&mut config.compiler_docs, compiler_docs);
         set(&mut config.library_docs_private_items, library_docs_private_items);
diff --git a/src/bootstrap/src/core/download.rs b/src/bootstrap/src/core/download.rs
index 185089a646b..27829eab937 100644
--- a/src/bootstrap/src/core/download.rs
+++ b/src/bootstrap/src/core/download.rs
@@ -578,7 +578,9 @@ impl Config {
             return;
         }
 
-        let cache_dst = self.out.join("cache");
+        let cache_dst =
+            self.bootstrap_cache_path.as_ref().cloned().unwrap_or_else(|| self.out.join("cache"));
+
         let cache_dir = cache_dst.join(key);
         if !cache_dir.exists() {
             t!(fs::create_dir_all(&cache_dir));
@@ -705,7 +707,9 @@ download-rustc = false
         let llvm_assertions = self.llvm_assertions;
 
         let cache_prefix = format!("llvm-{llvm_sha}-{llvm_assertions}");
-        let cache_dst = self.out.join("cache");
+        let cache_dst =
+            self.bootstrap_cache_path.as_ref().cloned().unwrap_or_else(|| self.out.join("cache"));
+
         let rustc_cache = cache_dst.join(cache_prefix);
         if !rustc_cache.exists() {
             t!(fs::create_dir_all(&rustc_cache));
diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs
index 9a50ad4437e..d166b84e51f 100644
--- a/src/bootstrap/src/utils/change_tracker.rs
+++ b/src/bootstrap/src/utils/change_tracker.rs
@@ -136,4 +136,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[
         severity: ChangeSeverity::Info,
         summary: "`x install` now skips providing tarball sources (under 'build/dist' path) to speed up the installation process.",
     },
+    ChangeInfo {
+        change_id: 121976,
+        severity: ChangeSeverity::Info,
+        summary: "A new `boostrap-cache-path` option has been introduced which can be utilized to modify the cache path for bootstrap.",
+    },
 ];
diff --git a/src/tools/build_helper/src/git.rs b/src/tools/build_helper/src/git.rs
index b91dc38e924..a3c857b0268 100644
--- a/src/tools/build_helper/src/git.rs
+++ b/src/tools/build_helper/src/git.rs
@@ -113,6 +113,7 @@ pub fn get_git_merge_base(
 
 /// Returns the files that have been modified in the current branch compared to the master branch.
 /// The `extensions` parameter can be used to filter the files by their extension.
+/// Does not include removed files.
 /// If `extensions` is empty, all files will be returned.
 pub fn get_git_modified_files(
     config: &GitConfig<'_>,
@@ -125,13 +126,19 @@ pub fn get_git_modified_files(
     if let Some(git_dir) = git_dir {
         git.current_dir(git_dir);
     }
-    let files = output_result(git.args(["diff-index", "--name-only", merge_base.trim()]))?
+    let files = output_result(git.args(["diff-index", "--name-status", merge_base.trim()]))?
         .lines()
-        .map(|s| s.trim().to_owned())
-        .filter(|f| {
-            Path::new(f).extension().map_or(false, |ext| {
+        .filter_map(|f| {
+            let (status, name) = f.trim().split_once(char::is_whitespace).unwrap();
+            if status == "D" {
+                None
+            } else if Path::new(name).extension().map_or(false, |ext| {
                 extensions.is_empty() || extensions.contains(&ext.to_str().unwrap())
-            })
+            }) {
+                Some(name.to_owned())
+            } else {
+                None
+            }
         })
         .collect();
     Ok(Some(files))
diff --git a/tests/ui/abi/compatibility.rs b/tests/ui/abi/compatibility.rs
index a4f60ea2684..3ee4542810c 100644
--- a/tests/ui/abi/compatibility.rs
+++ b/tests/ui/abi/compatibility.rs
@@ -39,7 +39,7 @@
 //@ revisions: loongarch64
 //@[loongarch64] compile-flags: --target loongarch64-unknown-linux-gnu
 //@[loongarch64] needs-llvm-components: loongarch
-//@[loongarch64] min-llvm-version: 17
+//@[loongarch64] min-llvm-version: 18
 //@ revisions: wasm
 //@[wasm] compile-flags: --target wasm32-unknown-unknown
 //@[wasm] needs-llvm-components: webassembly
diff --git a/tests/ui/check-cfg/mix.stderr b/tests/ui/check-cfg/mix.stderr
index 93333e5ef2a..007f9de0331 100644
--- a/tests/ui/check-cfg/mix.stderr
+++ b/tests/ui/check-cfg/mix.stderr
@@ -251,7 +251,7 @@ warning: unexpected `cfg` condition value: `zebra`
 LL |     cfg!(target_feature = "zebra");
    |          ^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = note: expected values for `target_feature` are: `10e60`, `2e3`, `3e3r1`, `3e3r2`, `3e3r3`, `3e7`, `7e10`, `a`, `aclass`, `adx`, `aes`, `altivec`, `alu32`, `atomics`, `avx`, `avx2`, `avx512bf16`, `avx512bitalg`, `avx512bw`, `avx512cd`, `avx512dq`, `avx512er`, `avx512f`, `avx512fp16`, `avx512ifma`, `avx512pf`, `avx512vbmi`, `avx512vbmi2`, `avx512vl`, `avx512vnni`, `avx512vp2intersect`, `avx512vpopcntdq`, `bf16`, `bmi1`, `bmi2` and 186 more
+   = note: expected values for `target_feature` are: `10e60`, `2e3`, `3e3r1`, `3e3r2`, `3e3r3`, `3e7`, `7e10`, `a`, `aclass`, `adx`, `aes`, `altivec`, `alu32`, `atomics`, `avx`, `avx2`, `avx512bf16`, `avx512bitalg`, `avx512bw`, `avx512cd`, `avx512dq`, `avx512er`, `avx512f`, `avx512fp16`, `avx512ifma`, `avx512pf`, `avx512vbmi`, `avx512vbmi2`, `avx512vl`, `avx512vnni`, `avx512vp2intersect`, `avx512vpopcntdq`, `bf16`, `bmi1`, `bmi2` and 187 more
    = note: see <https://doc.rust-lang.org/nightly/unstable-book/compiler-flags/check-cfg.html> for more information about checking conditional configuration
 
 warning: 27 warnings emitted
diff --git a/tests/ui/check-cfg/well-known-values.stderr b/tests/ui/check-cfg/well-known-values.stderr
index 86df829fe72..49674daac26 100644
--- a/tests/ui/check-cfg/well-known-values.stderr
+++ b/tests/ui/check-cfg/well-known-values.stderr
@@ -154,7 +154,7 @@ warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE`
 LL |     target_feature = "_UNEXPECTED_VALUE",
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = note: expected values for `target_feature` are: `10e60`, `2e3`, `3e3r1`, `3e3r2`, `3e3r3`, `3e7`, `7e10`, `a`, `aclass`, `adx`, `aes`, `altivec`, `alu32`, `atomics`, `avx`, `avx2`, `avx512bf16`, `avx512bitalg`, `avx512bw`, `avx512cd`, `avx512dq`, `avx512er`, `avx512f`, `avx512fp16`, `avx512ifma`, `avx512pf`, `avx512vbmi`, `avx512vbmi2`, `avx512vl`, `avx512vnni`, `avx512vp2intersect`, `avx512vpopcntdq`, `bf16`, `bmi1`, `bmi2`, `bti`, `bulk-memory`, `c`, `cache`, `cmpxchg16b`, `crc`, `crt-static`, `d`, `d32`, `dit`, `doloop`, `dotprod`, `dpb`, `dpb2`, `dsp`, `dsp1e2`, `dspe60`, `e`, `e1`, `e2`, `edsp`, `elrw`, `ermsb`, `exception-handling`, `f`, `f16c`, `f32mm`, `f64mm`, `fast-unaligned-access`, `fcma`, `fdivdu`, `fhm`, `flagm`, `float1e2`, `float1e3`, `float3e4`, `float7e60`, `floate1`, `fma`, `fp-armv8`, `fp16`, `fp64`, `fpuv2_df`, `fpuv2_sf`, `fpuv3_df`, `fpuv3_hf`, `fpuv3_hi`, `fpuv3_sf`, `frintts`, `fxsr`, `gfni`, `hard-float`, `hard-float-abi`, `hard-tp`, `high-registers`, `hvx`, `hvx-length128b`, `hwdiv`, `i8mm`, `jsconv`, `lahfsahf`, `lasx`, `lbt`, `lor`, `lse`, `lsx`, `lvz`, `lzcnt`, `m`, `mclass`, `movbe`, `mp`, `mp1e2`, `msa`, `mte`, `multivalue`, `mutable-globals`, `neon`, `nontrapping-fptoint`, `nvic`, `paca`, `pacg`, `pan`, `pclmulqdq`, `pmuv3`, `popcnt`, `power10-vector`, `power8-altivec`, `power8-vector`, `power9-altivec`, `power9-vector`, `prfchw`, `rand`, `ras`, `rclass`, `rcpc`, `rcpc2`, `rdm`, `rdrand`, `rdseed`, `reference-types`, `relax`, `relaxed-simd`, `rtm`, `sb`, `sha`, `sha2`, `sha3`, `sign-ext`, `simd128`, `sm4`, `spe`, `ssbs`, `sse`, `sse2`, `sse3`, `sse4.1`, `sse4.2`, `sse4a`, `ssse3`, `sve`, `sve2`, `sve2-aes`, `sve2-bitperm`, `sve2-sha3`, `sve2-sm4`, `tbm`, `thumb-mode`, `thumb2`, `tme`, `trust`, `trustzone`, `ual`, `v`, `v5te`, `v6`, `v6k`, `v6t2`, `v7`, `v8`, `v8.1a`, `v8.2a`, `v8.3a`, `v8.4a`, `v8.5a`, `v8.6a`, `v8.7a`, `vaes`, `vdsp2e60f`, `vdspv1`, `vdspv2`, `vfp2`, `vfp3`, `vfp4`, `vh`, `virt`, `virtualization`, `vpclmulqdq`, `vsx`, `xsave`, `xsavec`, `xsaveopt`, `xsaves`, `zba`, `zbb`, `zbc`, `zbkb`, `zbkc`, `zbkx`, `zbs`, `zdinx`, `zfh`, `zfhmin`, `zfinx`, `zhinx`, `zhinxmin`, `zk`, `zkn`, `zknd`, `zkne`, `zknh`, `zkr`, `zks`, `zksed`, `zksh`, `zkt`
+   = note: expected values for `target_feature` are: `10e60`, `2e3`, `3e3r1`, `3e3r2`, `3e3r3`, `3e7`, `7e10`, `a`, `aclass`, `adx`, `aes`, `altivec`, `alu32`, `atomics`, `avx`, `avx2`, `avx512bf16`, `avx512bitalg`, `avx512bw`, `avx512cd`, `avx512dq`, `avx512er`, `avx512f`, `avx512fp16`, `avx512ifma`, `avx512pf`, `avx512vbmi`, `avx512vbmi2`, `avx512vl`, `avx512vnni`, `avx512vp2intersect`, `avx512vpopcntdq`, `bf16`, `bmi1`, `bmi2`, `bti`, `bulk-memory`, `c`, `cache`, `cmpxchg16b`, `crc`, `crt-static`, `d`, `d32`, `dit`, `doloop`, `dotprod`, `dpb`, `dpb2`, `dsp`, `dsp1e2`, `dspe60`, `e`, `e1`, `e2`, `edsp`, `elrw`, `ermsb`, `exception-handling`, `f`, `f16c`, `f32mm`, `f64mm`, `fast-unaligned-access`, `fcma`, `fdivdu`, `fhm`, `flagm`, `float1e2`, `float1e3`, `float3e4`, `float7e60`, `floate1`, `fma`, `fp-armv8`, `fp16`, `fp64`, `fpuv2_df`, `fpuv2_sf`, `fpuv3_df`, `fpuv3_hf`, `fpuv3_hi`, `fpuv3_sf`, `frecipe`, `frintts`, `fxsr`, `gfni`, `hard-float`, `hard-float-abi`, `hard-tp`, `high-registers`, `hvx`, `hvx-length128b`, `hwdiv`, `i8mm`, `jsconv`, `lahfsahf`, `lasx`, `lbt`, `lor`, `lse`, `lsx`, `lvz`, `lzcnt`, `m`, `mclass`, `movbe`, `mp`, `mp1e2`, `msa`, `mte`, `multivalue`, `mutable-globals`, `neon`, `nontrapping-fptoint`, `nvic`, `paca`, `pacg`, `pan`, `pclmulqdq`, `pmuv3`, `popcnt`, `power10-vector`, `power8-altivec`, `power8-vector`, `power9-altivec`, `power9-vector`, `prfchw`, `rand`, `ras`, `rclass`, `rcpc`, `rcpc2`, `rdm`, `rdrand`, `rdseed`, `reference-types`, `relax`, `relaxed-simd`, `rtm`, `sb`, `sha`, `sha2`, `sha3`, `sign-ext`, `simd128`, `sm4`, `spe`, `ssbs`, `sse`, `sse2`, `sse3`, `sse4.1`, `sse4.2`, `sse4a`, `ssse3`, `sve`, `sve2`, `sve2-aes`, `sve2-bitperm`, `sve2-sha3`, `sve2-sm4`, `tbm`, `thumb-mode`, `thumb2`, `tme`, `trust`, `trustzone`, `ual`, `v`, `v5te`, `v6`, `v6k`, `v6t2`, `v7`, `v8`, `v8.1a`, `v8.2a`, `v8.3a`, `v8.4a`, `v8.5a`, `v8.6a`, `v8.7a`, `vaes`, `vdsp2e60f`, `vdspv1`, `vdspv2`, `vfp2`, `vfp3`, `vfp4`, `vh`, `virt`, `virtualization`, `vpclmulqdq`, `vsx`, `xsave`, `xsavec`, `xsaveopt`, `xsaves`, `zba`, `zbb`, `zbc`, `zbkb`, `zbkc`, `zbkx`, `zbs`, `zdinx`, `zfh`, `zfhmin`, `zfinx`, `zhinx`, `zhinxmin`, `zk`, `zkn`, `zknd`, `zkne`, `zknh`, `zkr`, `zks`, `zksed`, `zksh`, `zkt`
    = note: see <https://doc.rust-lang.org/nightly/unstable-book/compiler-flags/check-cfg.html> for more information about checking conditional configuration
 
 warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE`
diff --git a/tests/ui/impl-trait/in-trait/rpitit-cycle-in-generics-of.rs b/tests/ui/impl-trait/in-trait/rpitit-cycle-in-generics-of.rs
new file mode 100644
index 00000000000..882497d1015
--- /dev/null
+++ b/tests/ui/impl-trait/in-trait/rpitit-cycle-in-generics-of.rs
@@ -0,0 +1,18 @@
+//@ check-pass
+
+// Check that we don't hit a query cycle when:
+// 1. Computing generics_of, which requires...
+// 2. Calling resolve_bound_vars, which requires...
+// 3. Calling associated_items, which requires...
+// 4. Calling associated_type_for_impl_trait_in_trait, which requires...
+// 5. Computing generics_of, which cycles.
+
+pub trait Foo<'a> {
+    type Assoc;
+
+    fn demo<T>(other: T) -> impl Foo<'a, Assoc = Self::Assoc>
+    where
+        T: Foo<'a, Assoc = ()>;
+}
+
+fn main() {}
diff --git a/tests/ui/imports/auxiliary/aux-issue-121915.rs b/tests/ui/imports/auxiliary/aux-issue-121915.rs
new file mode 100644
index 00000000000..7f9f5bda79f
--- /dev/null
+++ b/tests/ui/imports/auxiliary/aux-issue-121915.rs
@@ -0,0 +1 @@
+pub fn item() {}
diff --git a/tests/ui/imports/redundant-import-issue-121915-2015.rs b/tests/ui/imports/redundant-import-issue-121915-2015.rs
new file mode 100644
index 00000000000..d41d190bb58
--- /dev/null
+++ b/tests/ui/imports/redundant-import-issue-121915-2015.rs
@@ -0,0 +1,11 @@
+//@ compile-flags: --extern aux_issue_121915 --edition 2015
+//@ aux-build: aux-issue-121915.rs
+
+extern crate aux_issue_121915;
+
+#[deny(unused_imports)]
+fn main() {
+    use aux_issue_121915;
+    //~^ ERROR the item `aux_issue_121915` is imported redundantly
+    aux_issue_121915::item();
+}
diff --git a/tests/ui/imports/redundant-import-issue-121915-2015.stderr b/tests/ui/imports/redundant-import-issue-121915-2015.stderr
new file mode 100644
index 00000000000..174ed4fb96b
--- /dev/null
+++ b/tests/ui/imports/redundant-import-issue-121915-2015.stderr
@@ -0,0 +1,17 @@
+error: the item `aux_issue_121915` is imported redundantly
+  --> $DIR/redundant-import-issue-121915-2015.rs:8:9
+   |
+LL | extern crate aux_issue_121915;
+   | ------------------------------ the item `aux_issue_121915` is already imported here
+...
+LL |     use aux_issue_121915;
+   |         ^^^^^^^^^^^^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/redundant-import-issue-121915-2015.rs:6:8
+   |
+LL | #[deny(unused_imports)]
+   |        ^^^^^^^^^^^^^^
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/imports/redundant-import-issue-121915.rs b/tests/ui/imports/redundant-import-issue-121915.rs
new file mode 100644
index 00000000000..237acc4af25
--- /dev/null
+++ b/tests/ui/imports/redundant-import-issue-121915.rs
@@ -0,0 +1,9 @@
+//@ compile-flags: --extern aux_issue_121915 --edition 2018
+//@ aux-build: aux-issue-121915.rs
+
+#[deny(unused_imports)]
+fn main() {
+    use aux_issue_121915;
+    //~^ ERROR the item `aux_issue_121915` is imported redundantly
+    aux_issue_121915::item();
+}
diff --git a/tests/ui/imports/redundant-import-issue-121915.stderr b/tests/ui/imports/redundant-import-issue-121915.stderr
new file mode 100644
index 00000000000..0047d7c3420
--- /dev/null
+++ b/tests/ui/imports/redundant-import-issue-121915.stderr
@@ -0,0 +1,14 @@
+error: the item `aux_issue_121915` is imported redundantly
+  --> $DIR/redundant-import-issue-121915.rs:6:9
+   |
+LL |     use aux_issue_121915;
+   |         ^^^^^^^^^^^^^^^^ the item `aux_issue_121915` is already defined by prelude
+   |
+note: the lint level is defined here
+  --> $DIR/redundant-import-issue-121915.rs:4:8
+   |
+LL | #[deny(unused_imports)]
+   |        ^^^^^^^^^^^^^^
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/imports/suggest-remove-issue-121315.rs b/tests/ui/imports/suggest-remove-issue-121315.rs
new file mode 100644
index 00000000000..63533480ec1
--- /dev/null
+++ b/tests/ui/imports/suggest-remove-issue-121315.rs
@@ -0,0 +1,40 @@
+//@ compile-flags: --edition 2021
+#![deny(unused_imports)]
+#![allow(dead_code)]
+
+fn test0() {
+    // Test remove FlatUnused
+    use std::convert::TryFrom;
+    //~^ ERROR the item `TryFrom` is imported redundantly
+    let _ = u32::try_from(5i32);
+}
+
+fn test1() {
+    // FIXME(yukang) Test remove NestedFullUnused
+    use std::convert::{TryFrom, TryInto};
+    //~^ ERROR the item `TryFrom` is imported redundantly
+    //~| ERROR the item `TryInto` is imported redundantly
+
+    let _ = u32::try_from(5i32);
+    let _a: i32 = u32::try_into(5u32).unwrap();
+}
+
+fn test2() {
+    // FIXME(yukang): Test remove both redundant and unused
+    use std::convert::{AsMut, Into};
+    //~^ ERROR unused import: `AsMut`
+    //~| ERROR the item `Into` is imported redundantly
+
+    let _a: u32 = (5u8).into();
+}
+
+fn test3() {
+    // Test remove NestedPartialUnused
+    use std::convert::{From, Infallible};
+    //~^ ERROR unused import: `From`
+
+    trait MyTrait {}
+    impl MyTrait for fn() -> Infallible {}
+}
+
+fn main() {}
diff --git a/tests/ui/imports/suggest-remove-issue-121315.stderr b/tests/ui/imports/suggest-remove-issue-121315.stderr
new file mode 100644
index 00000000000..dbd742f6c78
--- /dev/null
+++ b/tests/ui/imports/suggest-remove-issue-121315.stderr
@@ -0,0 +1,56 @@
+error: the item `TryFrom` is imported redundantly
+  --> $DIR/suggest-remove-issue-121315.rs:7:9
+   |
+LL |     use std::convert::TryFrom;
+   |         ^^^^^^^^^^^^^^^^^^^^^
+  --> $SRC_DIR/std/src/prelude/mod.rs:LL:COL
+   |
+   = note: the item `TryFrom` is already defined here
+   |
+note: the lint level is defined here
+  --> $DIR/suggest-remove-issue-121315.rs:2:9
+   |
+LL | #![deny(unused_imports)]
+   |         ^^^^^^^^^^^^^^
+
+error: the item `TryFrom` is imported redundantly
+  --> $DIR/suggest-remove-issue-121315.rs:14:24
+   |
+LL |     use std::convert::{TryFrom, TryInto};
+   |                        ^^^^^^^
+  --> $SRC_DIR/std/src/prelude/mod.rs:LL:COL
+   |
+   = note: the item `TryFrom` is already defined here
+
+error: the item `TryInto` is imported redundantly
+  --> $DIR/suggest-remove-issue-121315.rs:14:33
+   |
+LL |     use std::convert::{TryFrom, TryInto};
+   |                                 ^^^^^^^
+  --> $SRC_DIR/std/src/prelude/mod.rs:LL:COL
+   |
+   = note: the item `TryInto` is already defined here
+
+error: unused import: `AsMut`
+  --> $DIR/suggest-remove-issue-121315.rs:24:24
+   |
+LL |     use std::convert::{AsMut, Into};
+   |                        ^^^^^
+
+error: the item `Into` is imported redundantly
+  --> $DIR/suggest-remove-issue-121315.rs:24:31
+   |
+LL |     use std::convert::{AsMut, Into};
+   |                               ^^^^
+  --> $SRC_DIR/std/src/prelude/mod.rs:LL:COL
+   |
+   = note: the item `Into` is already defined here
+
+error: unused import: `From`
+  --> $DIR/suggest-remove-issue-121315.rs:33:24
+   |
+LL |     use std::convert::{From, Infallible};
+   |                        ^^^^
+
+error: aborting due to 6 previous errors
+