about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0578.md4
-rw-r--r--compiler/rustc_resolve/src/build_reduced_graph.rs21
-rw-r--r--compiler/rustc_resolve/src/diagnostics.rs15
-rw-r--r--compiler/rustc_resolve/src/ident.rs61
-rw-r--r--compiler/rustc_resolve/src/lib.rs80
-rw-r--r--src/bootstrap/src/core/build_steps/check.rs62
-rw-r--r--src/bootstrap/src/core/build_steps/clippy.rs299
-rw-r--r--src/bootstrap/src/core/builder/mod.rs64
-rw-r--r--src/bootstrap/src/core/builder/tests.rs133
-rw-r--r--src/bootstrap/src/core/config/config.rs4
-rw-r--r--src/bootstrap/src/utils/build_stamp.rs6
-rw-r--r--src/bootstrap/src/utils/change_tracker.rs5
-rw-r--r--src/ci/docker/host-x86_64/pr-check-2/Dockerfile2
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-6255.rs2
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-6255.stderr22
-rw-r--r--tests/ui/imports/issue-109148.rs3
-rw-r--r--tests/ui/imports/issue-109148.stderr40
-rw-r--r--tests/ui/macros/issue-78325-inconsistent-resolution.rs5
-rw-r--r--tests/ui/macros/issue-78325-inconsistent-resolution.stderr42
-rw-r--r--tests/ui/resolve/extern-prelude-speculative.rs10
-rw-r--r--tests/ui/resolve/visibility-indeterminate.rs2
-rw-r--r--tests/ui/resolve/visibility-indeterminate.stderr8
-rw-r--r--tests/ui/rust-2018/uniform-paths/deadlock.rs2
-rw-r--r--tests/ui/rust-2018/uniform-paths/deadlock.stderr8
24 files changed, 666 insertions, 234 deletions
diff --git a/compiler/rustc_error_codes/src/error_codes/E0578.md b/compiler/rustc_error_codes/src/error_codes/E0578.md
index fca89757287..78fabe855bb 100644
--- a/compiler/rustc_error_codes/src/error_codes/E0578.md
+++ b/compiler/rustc_error_codes/src/error_codes/E0578.md
@@ -1,8 +1,10 @@
+#### Note: this error code is no longer emitted by the compiler.
+
 A module cannot be found and therefore, the visibility cannot be determined.
 
 Erroneous code example:
 
-```compile_fail,E0578,edition2018
+```ignore (no longer emitted)
 foo!();
 
 pub (in ::Sea) struct Shark; // error!
diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs
index 988586334fa..580fa4f5b2c 100644
--- a/compiler/rustc_resolve/src/build_reduced_graph.rs
+++ b/compiler/rustc_resolve/src/build_reduced_graph.rs
@@ -971,40 +971,35 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
         let imported_binding = self.r.import(binding, import);
         if ident.name != kw::Underscore && parent == self.r.graph_root {
             let norm_ident = Macros20NormalizedIdent::new(ident);
+            // FIXME: this error is technically unnecessary now when extern prelude is split into
+            // two scopes, remove it with lang team approval.
             if let Some(entry) = self.r.extern_prelude.get(&norm_ident)
                 && expansion != LocalExpnId::ROOT
                 && orig_name.is_some()
-                && !entry.is_import()
+                && entry.item_binding.is_none()
             {
                 self.r.dcx().emit_err(
                     errors::MacroExpandedExternCrateCannotShadowExternArguments { span: item.span },
                 );
-                // `return` is intended to discard this binding because it's an
-                // unregistered ambiguity error which would result in a panic
-                // caused by inconsistency `path_res`
-                // more details: https://github.com/rust-lang/rust/pull/111761
-                return;
             }
 
             use indexmap::map::Entry;
             match self.r.extern_prelude.entry(norm_ident) {
                 Entry::Occupied(mut occupied) => {
                     let entry = occupied.get_mut();
-                    if let Some(old_binding) = entry.binding.get()
-                        && old_binding.is_import()
-                    {
+                    if entry.item_binding.is_some() {
                         let msg = format!("extern crate `{ident}` already in extern prelude");
                         self.r.tcx.dcx().span_delayed_bug(item.span, msg);
                     } else {
-                        // Binding from `extern crate` item in source code can replace
-                        // a binding from `--extern` on command line here.
-                        entry.binding.set(Some(imported_binding));
+                        entry.item_binding = Some(imported_binding);
                         entry.introduced_by_item = orig_name.is_some();
                     }
                     entry
                 }
                 Entry::Vacant(vacant) => vacant.insert(ExternPreludeEntry {
-                    binding: Cell::new(Some(imported_binding)),
+                    item_binding: Some(imported_binding),
+                    flag_binding: Cell::new(None),
+                    only_item: true,
                     introduced_by_item: true,
                 }),
             };
diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs
index a78cf028795..8526b233ba2 100644
--- a/compiler/rustc_resolve/src/diagnostics.rs
+++ b/compiler/rustc_resolve/src/diagnostics.rs
@@ -1096,12 +1096,14 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
                         );
                     }
                 }
-                Scope::ExternPrelude => {
+                Scope::ExternPreludeItems => {
+                    // Add idents from both item and flag scopes.
                     suggestions.extend(this.extern_prelude.keys().filter_map(|ident| {
                         let res = Res::Def(DefKind::Mod, CRATE_DEF_ID.to_def_id());
                         filter_fn(res).then_some(TypoSuggestion::typo_from_ident(ident.0, res))
                     }));
                 }
+                Scope::ExternPreludeFlags => {}
                 Scope::ToolPrelude => {
                     let res = Res::NonMacroAttr(NonMacroAttrKind::Tool);
                     suggestions.extend(
@@ -1873,14 +1875,20 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
         }
     }
 
-    fn ambiguity_diagnostics(&self, ambiguity_error: &AmbiguityError<'_>) -> AmbiguityErrorDiag {
+    fn ambiguity_diagnostics(&self, ambiguity_error: &AmbiguityError<'ra>) -> AmbiguityErrorDiag {
         let AmbiguityError { kind, ident, b1, b2, misc1, misc2, .. } = *ambiguity_error;
+        let extern_prelude_ambiguity = || {
+            self.extern_prelude.get(&Macros20NormalizedIdent::new(ident)).is_some_and(|entry| {
+                entry.item_binding == Some(b1) && entry.flag_binding.get() == Some(b2)
+            })
+        };
         let (b1, b2, misc1, misc2, swapped) = if b2.span.is_dummy() && !b1.span.is_dummy() {
             // We have to print the span-less alternative first, otherwise formatting looks bad.
             (b2, b1, misc2, misc1, true)
         } else {
             (b1, b2, misc1, misc2, false)
         };
+
         let could_refer_to = |b: NameBinding<'_>, misc: AmbiguityErrorMisc, also: &str| {
             let what = self.binding_description(b, ident, misc == AmbiguityErrorMisc::FromPrelude);
             let note_msg = format!("`{ident}` could{also} refer to {what}");
@@ -1896,7 +1904,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
                     "consider adding an explicit import of `{ident}` to disambiguate"
                 ))
             }
-            if b.is_extern_crate() && ident.span.at_least_rust_2018() {
+            if b.is_extern_crate() && ident.span.at_least_rust_2018() && !extern_prelude_ambiguity()
+            {
                 help_msgs.push(format!("use `::{ident}` to refer to this {thing} unambiguously"))
             }
             match misc {
diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs
index 87b3fc76c96..7ab932d9f2a 100644
--- a/compiler/rustc_resolve/src/ident.rs
+++ b/compiler/rustc_resolve/src/ident.rs
@@ -102,6 +102,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
             ScopeSet::All(ns)
             | ScopeSet::ModuleAndExternPrelude(ns, _)
             | ScopeSet::Late(ns, ..) => (ns, None),
+            ScopeSet::ExternPrelude => (TypeNS, None),
             ScopeSet::Macro(macro_kind) => (MacroNS, Some(macro_kind)),
         };
         let module = match scope_set {
@@ -111,8 +112,10 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
             _ => parent_scope.module.nearest_item_scope(),
         };
         let module_and_extern_prelude = matches!(scope_set, ScopeSet::ModuleAndExternPrelude(..));
+        let extern_prelude = matches!(scope_set, ScopeSet::ExternPrelude);
         let mut scope = match ns {
             _ if module_and_extern_prelude => Scope::Module(module, None),
+            _ if extern_prelude => Scope::ExternPreludeItems,
             TypeNS | ValueNS => Scope::Module(module, None),
             MacroNS => Scope::DeriveHelpers(parent_scope.expansion),
         };
@@ -143,7 +146,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
                 Scope::Module(..) => true,
                 Scope::MacroUsePrelude => use_prelude || rust_2015,
                 Scope::BuiltinAttrs => true,
-                Scope::ExternPrelude => use_prelude || module_and_extern_prelude,
+                Scope::ExternPreludeItems | Scope::ExternPreludeFlags => {
+                    use_prelude || module_and_extern_prelude || extern_prelude
+                }
                 Scope::ToolPrelude => use_prelude,
                 Scope::StdLibPrelude => use_prelude || ns == MacroNS,
                 Scope::BuiltinTypes => true,
@@ -182,7 +187,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
                 Scope::Module(..) if module_and_extern_prelude => match ns {
                     TypeNS => {
                         ctxt.adjust(ExpnId::root());
-                        Scope::ExternPrelude
+                        Scope::ExternPreludeItems
                     }
                     ValueNS | MacroNS => break,
                 },
@@ -199,7 +204,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
                         None => {
                             ctxt.adjust(ExpnId::root());
                             match ns {
-                                TypeNS => Scope::ExternPrelude,
+                                TypeNS => Scope::ExternPreludeItems,
                                 ValueNS => Scope::StdLibPrelude,
                                 MacroNS => Scope::MacroUsePrelude,
                             }
@@ -208,8 +213,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
                 }
                 Scope::MacroUsePrelude => Scope::StdLibPrelude,
                 Scope::BuiltinAttrs => break, // nowhere else to search
-                Scope::ExternPrelude if module_and_extern_prelude => break,
-                Scope::ExternPrelude => Scope::ToolPrelude,
+                Scope::ExternPreludeItems => Scope::ExternPreludeFlags,
+                Scope::ExternPreludeFlags if module_and_extern_prelude || extern_prelude => break,
+                Scope::ExternPreludeFlags => Scope::ToolPrelude,
                 Scope::ToolPrelude => Scope::StdLibPrelude,
                 Scope::StdLibPrelude => match ns {
                     TypeNS => Scope::BuiltinTypes,
@@ -413,6 +419,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
             ScopeSet::All(ns)
             | ScopeSet::ModuleAndExternPrelude(ns, _)
             | ScopeSet::Late(ns, ..) => (ns, None),
+            ScopeSet::ExternPrelude => (TypeNS, None),
             ScopeSet::Macro(macro_kind) => (MacroNS, Some(macro_kind)),
         };
 
@@ -429,6 +436,10 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
         // to detect potential ambiguities.
         let mut innermost_result: Option<(NameBinding<'_>, Flags)> = None;
         let mut determinacy = Determinacy::Determined;
+        // Shadowed bindings don't need to be marked as used or non-speculatively loaded.
+        macro finalize_scope() {
+            if innermost_result.is_none() { finalize } else { None }
+        }
 
         // Go through all the scopes and try to resolve the name.
         let break_result = self.visit_scopes(
@@ -494,7 +505,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
                         _ => Err(Determinacy::Determined),
                     },
                     Scope::Module(module, derive_fallback_lint_id) => {
-                        let (adjusted_parent_scope, finalize) =
+                        // FIXME: use `finalize_scope` here.
+                        let (adjusted_parent_scope, adjusted_finalize) =
                             if matches!(scope_set, ScopeSet::ModuleAndExternPrelude(..)) {
                                 (parent_scope, finalize)
                             } else {
@@ -513,7 +525,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
                             } else {
                                 Shadowing::Restricted
                             },
-                            finalize,
+                            adjusted_finalize,
                             ignore_binding,
                             ignore_import,
                         );
@@ -561,14 +573,21 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
                         Some(binding) => Ok((*binding, Flags::empty())),
                         None => Err(Determinacy::Determined),
                     },
-                    Scope::ExternPrelude => {
-                        match this.reborrow().extern_prelude_get(ident, finalize.is_some()) {
+                    Scope::ExternPreludeItems => {
+                        // FIXME: use `finalize_scope` here.
+                        match this.reborrow().extern_prelude_get_item(ident, finalize.is_some()) {
                             Some(binding) => Ok((binding, Flags::empty())),
                             None => Err(Determinacy::determined(
                                 this.graph_root.unexpanded_invocations.borrow().is_empty(),
                             )),
                         }
                     }
+                    Scope::ExternPreludeFlags => {
+                        match this.extern_prelude_get_flag(ident, finalize_scope!().is_some()) {
+                            Some(binding) => Ok((binding, Flags::empty())),
+                            None => Err(Determinacy::Determined),
+                        }
+                    }
                     Scope::ToolPrelude => match this.registered_tool_bindings.get(&ident) {
                         Some(binding) => Ok((*binding, Flags::empty())),
                         None => Err(Determinacy::Determined),
@@ -599,8 +618,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
                             if matches!(ident.name, sym::f16)
                                 && !this.tcx.features().f16()
                                 && !ident.span.allows_unstable(sym::f16)
-                                && finalize.is_some()
-                                && innermost_result.is_none()
+                                && finalize_scope!().is_some()
                             {
                                 feature_err(
                                     this.tcx.sess,
@@ -613,8 +631,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
                             if matches!(ident.name, sym::f128)
                                 && !this.tcx.features().f128()
                                 && !ident.span.allows_unstable(sym::f128)
-                                && finalize.is_some()
-                                && innermost_result.is_none()
+                                && finalize_scope!().is_some()
                             {
                                 feature_err(
                                     this.tcx.sess,
@@ -819,15 +836,17 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
                 assert_eq!(shadowing, Shadowing::Unrestricted);
                 return if ns != TypeNS {
                     Err((Determined, Weak::No))
-                } else if let Some(binding) =
-                    self.reborrow().extern_prelude_get(ident, finalize.is_some())
-                {
-                    Ok(binding)
-                } else if !self.graph_root.unexpanded_invocations.borrow().is_empty() {
-                    // Macro-expanded `extern crate` items can add names to extern prelude.
-                    Err((Undetermined, Weak::No))
                 } else {
-                    Err((Determined, Weak::No))
+                    let binding = self.early_resolve_ident_in_lexical_scope(
+                        ident,
+                        ScopeSet::ExternPrelude,
+                        parent_scope,
+                        finalize,
+                        finalize.is_some(),
+                        ignore_binding,
+                        ignore_import,
+                    );
+                    return binding.map_err(|determinacy| (determinacy, Weak::No));
                 };
             }
             ModuleOrUniformRoot::CurrentScope => {
diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs
index 797f4f619e3..cc30939f5e9 100644
--- a/compiler/rustc_resolve/src/lib.rs
+++ b/compiler/rustc_resolve/src/lib.rs
@@ -15,6 +15,7 @@
 #![feature(arbitrary_self_types)]
 #![feature(assert_matches)]
 #![feature(box_patterns)]
+#![feature(decl_macro)]
 #![feature(if_let_guard)]
 #![feature(iter_intersperse)]
 #![feature(rustc_attrs)]
@@ -114,34 +115,46 @@ impl Determinacy {
 }
 
 /// A specific scope in which a name can be looked up.
-/// This enum is currently used only for early resolution (imports and macros),
-/// but not for late resolution yet.
 #[derive(Clone, Copy, Debug)]
 enum Scope<'ra> {
+    /// Inert attributes registered by derive macros.
     DeriveHelpers(LocalExpnId),
+    /// Inert attributes registered by derive macros, but used before they are actually declared.
+    /// This scope will exist until the compatibility lint `LEGACY_DERIVE_HELPERS`
+    /// is turned into a hard error.
     DeriveHelpersCompat,
+    /// Textual `let`-like scopes introduced by `macro_rules!` items.
     MacroRules(MacroRulesScopeRef<'ra>),
-    // The node ID is for reporting the `PROC_MACRO_DERIVE_RESOLUTION_FALLBACK`
-    // lint if it should be reported.
+    /// Names declared in the given module.
+    /// The node ID is for reporting the `PROC_MACRO_DERIVE_RESOLUTION_FALLBACK`
+    /// lint if it should be reported.
     Module(Module<'ra>, Option<NodeId>),
+    /// Names introduced by `#[macro_use]` attributes on `extern crate` items.
     MacroUsePrelude,
+    /// Built-in attributes.
     BuiltinAttrs,
-    ExternPrelude,
+    /// Extern prelude names introduced by `extern crate` items.
+    ExternPreludeItems,
+    /// Extern prelude names introduced by `--extern` flags.
+    ExternPreludeFlags,
+    /// Tool modules introduced with `#![register_tool]`.
     ToolPrelude,
+    /// Standard library prelude introduced with an internal `#[prelude_import]` import.
     StdLibPrelude,
+    /// Built-in types.
     BuiltinTypes,
 }
 
 /// Names from different contexts may want to visit different subsets of all specific scopes
 /// with different restrictions when looking up the resolution.
-/// This enum is currently used only for early resolution (imports and macros),
-/// but not for late resolution yet.
 #[derive(Clone, Copy, Debug)]
 enum ScopeSet<'ra> {
     /// All scopes with the given namespace.
     All(Namespace),
     /// A module, then extern prelude (used for mixed 2015-2018 mode in macros).
     ModuleAndExternPrelude(Namespace, Module<'ra>),
+    /// Just two extern prelude scopes.
+    ExternPrelude,
     /// All scopes with macro namespace and the given macro kind restriction.
     Macro(MacroKind),
     /// All scopes with the given namespace, used for partially performing late resolution.
@@ -1013,16 +1026,18 @@ impl<'ra> NameBindingData<'ra> {
 
 #[derive(Default, Clone)]
 struct ExternPreludeEntry<'ra> {
-    binding: Cell<Option<NameBinding<'ra>>>,
+    /// Binding from an `extern crate` item.
+    item_binding: Option<NameBinding<'ra>>,
+    /// Binding from an `--extern` flag, lazily populated on first use.
+    flag_binding: Cell<Option<NameBinding<'ra>>>,
+    /// There was no `--extern` flag introducing this name,
+    /// `flag_binding` doesn't need to be populated.
+    only_item: bool,
+    /// `item_binding` is non-redundant, happens either when `only_item` is true,
+    /// or when `extern crate` introducing `item_binding` used renaming.
     introduced_by_item: bool,
 }
 
-impl ExternPreludeEntry<'_> {
-    fn is_import(&self) -> bool {
-        self.binding.get().is_some_and(|binding| binding.is_import())
-    }
-}
-
 struct DeriveData {
     resolutions: Vec<DeriveResolution>,
     helper_attrs: Vec<(usize, Ident)>,
@@ -1889,7 +1904,10 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
                         this.get_mut().traits_in_module(module, assoc_item, &mut found_traits);
                     }
                 }
-                Scope::ExternPrelude | Scope::ToolPrelude | Scope::BuiltinTypes => {}
+                Scope::ExternPreludeItems
+                | Scope::ExternPreludeFlags
+                | Scope::ToolPrelude
+                | Scope::BuiltinTypes => {}
                 _ => unreachable!(),
             }
             None::<()>
@@ -2054,7 +2072,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
             // but not introduce it, as used if they are accessed from lexical scope.
             if used == Used::Scope {
                 if let Some(entry) = self.extern_prelude.get(&Macros20NormalizedIdent::new(ident)) {
-                    if !entry.introduced_by_item && entry.binding.get() == Some(used_binding) {
+                    if !entry.introduced_by_item && entry.item_binding == Some(used_binding) {
                         return;
                     }
                 }
@@ -2210,26 +2228,30 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
         }
     }
 
-    fn extern_prelude_get<'r>(
+    fn extern_prelude_get_item<'r>(
         mut self: CmResolver<'r, 'ra, 'tcx>,
         ident: Ident,
         finalize: bool,
     ) -> Option<NameBinding<'ra>> {
-        let mut record_use = None;
         let entry = self.extern_prelude.get(&Macros20NormalizedIdent::new(ident));
-        let binding = entry.and_then(|entry| match entry.binding.get() {
-            Some(binding) if binding.is_import() => {
-                if finalize {
-                    record_use = Some(binding);
-                }
-                Some(binding)
+        entry.and_then(|entry| entry.item_binding).map(|binding| {
+            if finalize {
+                self.get_mut().record_use(ident, binding, Used::Scope);
             }
+            binding
+        })
+    }
+
+    fn extern_prelude_get_flag(&self, ident: Ident, finalize: bool) -> Option<NameBinding<'ra>> {
+        let entry = self.extern_prelude.get(&Macros20NormalizedIdent::new(ident));
+        entry.and_then(|entry| match entry.flag_binding.get() {
             Some(binding) => {
                 if finalize {
                     self.cstore_mut().process_path_extern(self.tcx, ident.name, ident.span);
                 }
                 Some(binding)
             }
+            None if entry.only_item => None,
             None => {
                 let crate_id = if finalize {
                     self.cstore_mut().process_path_extern(self.tcx, ident.name, ident.span)
@@ -2241,19 +2263,13 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
                         let res = Res::Def(DefKind::Mod, crate_id.as_def_id());
                         let binding =
                             self.arenas.new_pub_res_binding(res, DUMMY_SP, LocalExpnId::ROOT);
-                        entry.binding.set(Some(binding));
+                        entry.flag_binding.set(Some(binding));
                         Some(binding)
                     }
                     None => finalize.then_some(self.dummy_binding),
                 }
             }
-        });
-
-        if let Some(binding) = record_use {
-            self.get_mut().record_use(ident, binding, Used::Scope);
-        }
-
-        binding
+        })
     }
 
     /// Rustdoc uses this to resolve doc link paths in a recoverable way. `PathResult<'a>`
diff --git a/src/bootstrap/src/core/build_steps/check.rs b/src/bootstrap/src/core/build_steps/check.rs
index 0cbf8f55e99..6d393446d45 100644
--- a/src/bootstrap/src/core/build_steps/check.rs
+++ b/src/bootstrap/src/core/build_steps/check.rs
@@ -30,10 +30,6 @@ pub struct Std {
 
 impl Std {
     const CRATE_OR_DEPS: &[&str] = &["sysroot", "coretests", "alloctests"];
-
-    pub fn new(build_compiler: Compiler, target: TargetSelection) -> Self {
-        Self { build_compiler, target, crates: vec![] }
-    }
 }
 
 impl Step for Std {
@@ -168,12 +164,8 @@ pub struct Rustc {
 }
 
 impl Rustc {
-    pub fn new(builder: &Builder<'_>, build_compiler: Compiler, target: TargetSelection) -> Self {
-        let crates = builder
-            .in_tree_crates("rustc-main", Some(target))
-            .into_iter()
-            .map(|krate| krate.name.to_string())
-            .collect();
+    pub fn new(builder: &Builder<'_>, target: TargetSelection, crates: Vec<String>) -> Self {
+        let build_compiler = prepare_compiler_for_check(builder, target, Mode::Rustc);
         Self { build_compiler, target, crates }
     }
 }
@@ -189,11 +181,7 @@ impl Step for Rustc {
 
     fn make_run(run: RunConfig<'_>) {
         let crates = run.make_run_crates(Alias::Compiler);
-        run.builder.ensure(Rustc {
-            target: run.target,
-            build_compiler: prepare_compiler_for_check(run.builder, run.target, Mode::Rustc),
-            crates,
-        });
+        run.builder.ensure(Rustc::new(run.builder, run.target, crates));
     }
 
     /// Check the compiler.
@@ -207,15 +195,6 @@ impl Step for Rustc {
         let build_compiler = self.build_compiler;
         let target = self.target;
 
-        // Build host std for compiling build scripts
-        builder.std(build_compiler, build_compiler.host);
-
-        // Build target std so that the checked rustc can link to it during the check
-        // FIXME: maybe we can a way to only do a check of std here?
-        // But for that we would have to copy the stdlib rmetas to the sysroot of the build
-        // compiler, which conflicts with std rlibs, if we also build std.
-        builder.std(build_compiler, target);
-
         let mut cargo = builder::Cargo::new(
             builder,
             build_compiler,
@@ -253,12 +232,18 @@ impl Step for Rustc {
     }
 
     fn metadata(&self) -> Option<StepMetadata> {
-        Some(StepMetadata::check("rustc", self.target).built_by(self.build_compiler))
+        let metadata = StepMetadata::check("rustc", self.target).built_by(self.build_compiler);
+        let metadata = if self.crates.is_empty() {
+            metadata
+        } else {
+            metadata.with_metadata(format!("({} crates)", self.crates.len()))
+        };
+        Some(metadata)
     }
 }
 
 /// Prepares a compiler that will check something with the given `mode`.
-fn prepare_compiler_for_check(
+pub fn prepare_compiler_for_check(
     builder: &Builder<'_>,
     target: TargetSelection,
     mode: Mode,
@@ -289,11 +274,13 @@ fn prepare_compiler_for_check(
             build_compiler
         }
         Mode::ToolRustc | Mode::Codegen => {
-            // FIXME: this is a hack, see description of Mode::Rustc below
-            let stage = if host == target { builder.top_stage - 1 } else { builder.top_stage };
-            // When checking tool stage N, we check it with compiler stage N-1
-            let build_compiler = builder.compiler(stage, host);
-            builder.ensure(Rustc::new(builder, build_compiler, target));
+            // Check Rustc to produce the required rmeta artifacts for rustc_private, and then
+            // return the build compiler that was used to check rustc.
+            // We do not need to check examples/tests/etc. of Rustc for rustc_private, so we pass
+            // an empty set of crates, which will avoid using `cargo -p`.
+            let check = Rustc::new(builder, target, vec![]);
+            let build_compiler = check.build_compiler;
+            builder.ensure(check);
             build_compiler
         }
         Mode::Rustc => {
@@ -305,7 +292,18 @@ fn prepare_compiler_for_check(
             // FIXME: remove this and either fix cross-compilation check on stage 2 (which has a
             // myriad of other problems) or disable cross-checking on stage 1.
             let stage = if host == target { builder.top_stage - 1 } else { builder.top_stage };
-            builder.compiler(stage, host)
+            let build_compiler = builder.compiler(stage, host);
+
+            // Build host std for compiling build scripts
+            builder.std(build_compiler, build_compiler.host);
+
+            // Build target std so that the checked rustc can link to it during the check
+            // FIXME: maybe we can a way to only do a check of std here?
+            // But for that we would have to copy the stdlib rmetas to the sysroot of the build
+            // compiler, which conflicts with std rlibs, if we also build std.
+            builder.std(build_compiler, target);
+
+            build_compiler
         }
         Mode::Std => {
             // When checking std stage N, we want to do it with the stage N compiler
diff --git a/src/bootstrap/src/core/build_steps/clippy.rs b/src/bootstrap/src/core/build_steps/clippy.rs
index 4d734fe5c66..23d9a032eb9 100644
--- a/src/bootstrap/src/core/build_steps/clippy.rs
+++ b/src/bootstrap/src/core/build_steps/clippy.rs
@@ -1,14 +1,29 @@
 //! Implementation of running clippy on the compiler, standard library and various tools.
+//!
+//! This serves a double purpose:
+//! - The first is to run Clippy itself on in-tree code, in order to test and dogfood it.
+//! - The second is to actually lint the in-tree codebase on CI, with a hard-coded set of rules,
+//!   which is performed by the `x clippy ci` command.
+//!
+//! In order to prepare a build compiler for running clippy, use the
+//! [prepare_compiler_for_check] function. That prepares a
+//! compiler and a standard library
+//! for running Clippy. The second part (actually building Clippy) is performed inside
+//! [Builder::cargo_clippy_cmd]. It would be nice if this was more explicit, and we actually had
+//! to pass a prebuilt Clippy from the outside when running `cargo clippy`, but that would be
+//! (as usual) a massive undertaking/refactoring.
+
+use build_helper::exit;
 
-use super::check;
 use super::compile::{run_cargo, rustc_cargo, std_cargo};
 use super::tool::{SourceType, prepare_tool_cargo};
 use crate::builder::{Builder, ShouldRun};
+use crate::core::build_steps::check::prepare_compiler_for_check;
 use crate::core::build_steps::compile::std_crates_for_run_make;
 use crate::core::builder;
-use crate::core::builder::{Alias, Kind, RunConfig, Step, crate_description};
+use crate::core::builder::{Alias, Kind, RunConfig, Step, StepMetadata, crate_description};
 use crate::utils::build_stamp::{self, BuildStamp};
-use crate::{Mode, Subcommand, TargetSelection};
+use crate::{Compiler, Mode, Subcommand, TargetSelection};
 
 /// Disable the most spammy clippy lints
 const IGNORED_RULES_FOR_STD_AND_RUSTC: &[&str] = &[
@@ -121,12 +136,38 @@ impl LintConfig {
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct Std {
-    pub target: TargetSelection,
+    build_compiler: Compiler,
+    target: TargetSelection,
     config: LintConfig,
     /// Whether to lint only a subset of crates.
     crates: Vec<String>,
 }
 
+impl Std {
+    fn new(
+        builder: &Builder<'_>,
+        target: TargetSelection,
+        config: LintConfig,
+        crates: Vec<String>,
+    ) -> Self {
+        Self {
+            build_compiler: builder.compiler(builder.top_stage, builder.host_target),
+            target,
+            config,
+            crates,
+        }
+    }
+
+    fn from_build_compiler(
+        build_compiler: Compiler,
+        target: TargetSelection,
+        config: LintConfig,
+        crates: Vec<String>,
+    ) -> Self {
+        Self { build_compiler, target, config, crates }
+    }
+}
+
 impl Step for Std {
     type Output = ();
     const DEFAULT: bool = true;
@@ -138,12 +179,12 @@ impl Step for Std {
     fn make_run(run: RunConfig<'_>) {
         let crates = std_crates_for_run_make(&run);
         let config = LintConfig::new(run.builder);
-        run.builder.ensure(Std { target: run.target, config, crates });
+        run.builder.ensure(Std::new(run.builder, run.target, config, crates));
     }
 
     fn run(self, builder: &Builder<'_>) {
         let target = self.target;
-        let build_compiler = builder.compiler(builder.top_stage, builder.config.host_target);
+        let build_compiler = self.build_compiler;
 
         let mut cargo = builder::Cargo::new(
             builder,
@@ -178,16 +219,41 @@ impl Step for Std {
             false,
         );
     }
+
+    fn metadata(&self) -> Option<StepMetadata> {
+        Some(StepMetadata::clippy("std", self.target).built_by(self.build_compiler))
+    }
 }
 
+/// Lints the compiler.
+///
+/// This will build Clippy with the `build_compiler` and use it to lint
+/// in-tree rustc.
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct Rustc {
-    pub target: TargetSelection,
+    build_compiler: Compiler,
+    target: TargetSelection,
     config: LintConfig,
     /// Whether to lint only a subset of crates.
     crates: Vec<String>,
 }
 
+impl Rustc {
+    fn new(
+        builder: &Builder<'_>,
+        target: TargetSelection,
+        config: LintConfig,
+        crates: Vec<String>,
+    ) -> Self {
+        Self {
+            build_compiler: prepare_compiler_for_check(builder, target, Mode::Rustc),
+            target,
+            config,
+            crates,
+        }
+    }
+}
+
 impl Step for Rustc {
     type Output = ();
     const ONLY_HOSTS: bool = true;
@@ -198,33 +264,16 @@ impl Step for Rustc {
     }
 
     fn make_run(run: RunConfig<'_>) {
+        let builder = run.builder;
         let crates = run.make_run_crates(Alias::Compiler);
         let config = LintConfig::new(run.builder);
-        run.builder.ensure(Rustc { target: run.target, config, crates });
+        run.builder.ensure(Rustc::new(builder, run.target, config, crates));
     }
 
-    /// Lints the compiler.
-    ///
-    /// This will lint the compiler for a particular stage of the build using
-    /// the `compiler` targeting the `target` architecture.
     fn run(self, builder: &Builder<'_>) {
-        let build_compiler = builder.compiler(builder.top_stage, builder.config.host_target);
+        let build_compiler = self.build_compiler;
         let target = self.target;
 
-        if !builder.download_rustc() {
-            if build_compiler.stage != 0 {
-                // If we're not in stage 0, then we won't have a std from the beta
-                // compiler around. That means we need to make sure there's one in
-                // the sysroot for the compiler to find. Otherwise, we're going to
-                // fail when building crates that need to generate code (e.g., build
-                // scripts and their dependencies).
-                builder.std(build_compiler, build_compiler.host);
-                builder.std(build_compiler, target);
-            } else {
-                builder.ensure(check::Std::new(build_compiler, target));
-            }
-        }
-
         let mut cargo = builder::Cargo::new(
             builder,
             build_compiler,
@@ -261,11 +310,85 @@ impl Step for Rustc {
             false,
         );
     }
+
+    fn metadata(&self) -> Option<StepMetadata> {
+        Some(StepMetadata::clippy("rustc", self.target).built_by(self.build_compiler))
+    }
+}
+
+#[derive(Debug, Clone, Hash, PartialEq, Eq)]
+pub struct CodegenGcc {
+    build_compiler: Compiler,
+    target: TargetSelection,
+    config: LintConfig,
+}
+
+impl CodegenGcc {
+    fn new(builder: &Builder<'_>, target: TargetSelection, config: LintConfig) -> Self {
+        Self {
+            build_compiler: prepare_compiler_for_check(builder, target, Mode::Codegen),
+            target,
+            config,
+        }
+    }
+}
+
+impl Step for CodegenGcc {
+    type Output = ();
+
+    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
+        run.alias("rustc_codegen_gcc")
+    }
+
+    fn make_run(run: RunConfig<'_>) {
+        let builder = run.builder;
+        let config = LintConfig::new(builder);
+        builder.ensure(CodegenGcc::new(builder, run.target, config));
+    }
+
+    fn run(self, builder: &Builder<'_>) -> Self::Output {
+        let build_compiler = self.build_compiler;
+        let target = self.target;
+
+        let cargo = prepare_tool_cargo(
+            builder,
+            build_compiler,
+            Mode::Codegen,
+            target,
+            Kind::Clippy,
+            "compiler/rustc_codegen_gcc",
+            SourceType::InTree,
+            &[],
+        );
+
+        let _guard =
+            builder.msg(Kind::Clippy, "rustc_codegen_gcc", Mode::ToolRustc, build_compiler, target);
+
+        let stamp = BuildStamp::new(&builder.cargo_out(build_compiler, Mode::Codegen, target))
+            .with_prefix("rustc_codegen_gcc-check");
+
+        run_cargo(
+            builder,
+            cargo,
+            lint_args(builder, &self.config, &[]),
+            &stamp,
+            vec![],
+            true,
+            false,
+        );
+    }
+
+    fn metadata(&self) -> Option<StepMetadata> {
+        Some(StepMetadata::clippy("rustc_codegen_gcc", self.target).built_by(self.build_compiler))
+    }
 }
 
 macro_rules! lint_any {
     ($(
-        $name:ident, $path:expr, $readable_name:expr
+        $name:ident,
+        $path:expr,
+        $readable_name:expr,
+        $mode:expr
         $(,lint_by_default = $lint_by_default:expr)*
         ;
     )+) => {
@@ -273,7 +396,8 @@ macro_rules! lint_any {
 
         #[derive(Debug, Clone, Hash, PartialEq, Eq)]
         pub struct $name {
-            pub target: TargetSelection,
+            build_compiler: Compiler,
+            target: TargetSelection,
             config: LintConfig,
         }
 
@@ -288,23 +412,19 @@ macro_rules! lint_any {
             fn make_run(run: RunConfig<'_>) {
                 let config = LintConfig::new(run.builder);
                 run.builder.ensure($name {
+                    build_compiler: prepare_compiler_for_check(run.builder, run.target, $mode),
                     target: run.target,
                     config,
                 });
             }
 
             fn run(self, builder: &Builder<'_>) -> Self::Output {
-                let build_compiler = builder.compiler(builder.top_stage, builder.config.host_target);
+                let build_compiler = self.build_compiler;
                 let target = self.target;
-
-                if !builder.download_rustc() {
-                    builder.ensure(check::Rustc::new(builder, build_compiler, target));
-                };
-
                 let cargo = prepare_tool_cargo(
                     builder,
                     build_compiler,
-                    Mode::ToolRustc,
+                    $mode,
                     target,
                     Kind::Clippy,
                     $path,
@@ -315,13 +435,13 @@ macro_rules! lint_any {
                 let _guard = builder.msg(
                     Kind::Clippy,
                     $readable_name,
-                    Mode::ToolRustc,
+                    $mode,
                     build_compiler,
                     target,
                 );
 
                 let stringified_name = stringify!($name).to_lowercase();
-                let stamp = BuildStamp::new(&builder.cargo_out(build_compiler, Mode::ToolRustc, target))
+                let stamp = BuildStamp::new(&builder.cargo_out(build_compiler, $mode, target))
                     .with_prefix(&format!("{}-check", stringified_name));
 
                 run_cargo(
@@ -334,38 +454,44 @@ macro_rules! lint_any {
                     false,
                 );
             }
+
+            fn metadata(&self) -> Option<StepMetadata> {
+                Some(StepMetadata::clippy($readable_name, self.target).built_by(self.build_compiler))
+            }
         }
         )+
     }
 }
 
+// Note: we use ToolTarget instead of ToolBootstrap here, to allow linting in-tree host tools
+// using the in-tree Clippy. Because Mode::ToolBootstrap would always use stage 0 rustc/Clippy.
 lint_any!(
-    Bootstrap, "src/bootstrap", "bootstrap";
-    BuildHelper, "src/build_helper", "build_helper";
-    BuildManifest, "src/tools/build-manifest", "build-manifest";
-    CargoMiri, "src/tools/miri/cargo-miri", "cargo-miri";
-    Clippy, "src/tools/clippy", "clippy";
-    CollectLicenseMetadata, "src/tools/collect-license-metadata", "collect-license-metadata";
-    CodegenGcc, "compiler/rustc_codegen_gcc", "rustc-codegen-gcc";
-    Compiletest, "src/tools/compiletest", "compiletest";
-    CoverageDump, "src/tools/coverage-dump", "coverage-dump";
-    Jsondocck, "src/tools/jsondocck", "jsondocck";
-    Jsondoclint, "src/tools/jsondoclint", "jsondoclint";
-    LintDocs, "src/tools/lint-docs", "lint-docs";
-    LlvmBitcodeLinker, "src/tools/llvm-bitcode-linker", "llvm-bitcode-linker";
-    Miri, "src/tools/miri", "miri";
-    MiroptTestTools, "src/tools/miropt-test-tools", "miropt-test-tools";
-    OptDist, "src/tools/opt-dist", "opt-dist";
-    RemoteTestClient, "src/tools/remote-test-client", "remote-test-client";
-    RemoteTestServer, "src/tools/remote-test-server", "remote-test-server";
-    RustAnalyzer, "src/tools/rust-analyzer", "rust-analyzer";
-    Rustdoc, "src/librustdoc", "clippy";
-    Rustfmt, "src/tools/rustfmt", "rustfmt";
-    RustInstaller, "src/tools/rust-installer", "rust-installer";
-    Tidy, "src/tools/tidy", "tidy";
-    TestFloatParse, "src/tools/test-float-parse", "test-float-parse";
+    Bootstrap, "src/bootstrap", "bootstrap", Mode::ToolTarget;
+    BuildHelper, "src/build_helper", "build_helper", Mode::ToolTarget;
+    BuildManifest, "src/tools/build-manifest", "build-manifest", Mode::ToolTarget;
+    CargoMiri, "src/tools/miri/cargo-miri", "cargo-miri", Mode::ToolRustc;
+    Clippy, "src/tools/clippy", "clippy", Mode::ToolRustc;
+    CollectLicenseMetadata, "src/tools/collect-license-metadata", "collect-license-metadata", Mode::ToolTarget;
+    Compiletest, "src/tools/compiletest", "compiletest", Mode::ToolTarget;
+    CoverageDump, "src/tools/coverage-dump", "coverage-dump", Mode::ToolTarget;
+    Jsondocck, "src/tools/jsondocck", "jsondocck", Mode::ToolTarget;
+    Jsondoclint, "src/tools/jsondoclint", "jsondoclint", Mode::ToolTarget;
+    LintDocs, "src/tools/lint-docs", "lint-docs", Mode::ToolTarget;
+    LlvmBitcodeLinker, "src/tools/llvm-bitcode-linker", "llvm-bitcode-linker", Mode::ToolTarget;
+    Miri, "src/tools/miri", "miri", Mode::ToolRustc;
+    MiroptTestTools, "src/tools/miropt-test-tools", "miropt-test-tools", Mode::ToolTarget;
+    OptDist, "src/tools/opt-dist", "opt-dist", Mode::ToolTarget;
+    RemoteTestClient, "src/tools/remote-test-client", "remote-test-client", Mode::ToolTarget;
+    RemoteTestServer, "src/tools/remote-test-server", "remote-test-server", Mode::ToolTarget;
+    RustAnalyzer, "src/tools/rust-analyzer", "rust-analyzer", Mode::ToolRustc;
+    Rustdoc, "src/librustdoc", "clippy", Mode::ToolRustc;
+    Rustfmt, "src/tools/rustfmt", "rustfmt", Mode::ToolRustc;
+    RustInstaller, "src/tools/rust-installer", "rust-installer", Mode::ToolTarget;
+    Tidy, "src/tools/tidy", "tidy", Mode::ToolTarget;
+    TestFloatParse, "src/tools/test-float-parse", "test-float-parse", Mode::ToolStd;
 );
 
+/// Runs Clippy on in-tree sources of selected projects using in-tree CLippy.
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct CI {
     target: TargetSelection,
@@ -386,7 +512,21 @@ impl Step for CI {
     }
 
     fn run(self, builder: &Builder<'_>) -> Self::Output {
+        if builder.top_stage != 2 {
+            eprintln!("ERROR: `x clippy ci` should always be executed with --stage 2");
+            exit!(1);
+        }
+
+        // We want to check in-tree source using in-tree clippy. However, if we naively did
+        // a stage 2 `x clippy ci`, it would *build* a stage 2 rustc, in order to lint stage 2
+        // std, which is wasteful.
+        // So we want to lint stage 2 [bootstrap/rustc/...], but only stage 1 std rustc_codegen_gcc.
+        // We thus construct the compilers in this step manually, to optimize the number of
+        // steps that get built.
+
         builder.ensure(Bootstrap {
+            // This will be the stage 1 compiler
+            build_compiler: prepare_compiler_for_check(builder, self.target, Mode::ToolTarget),
             target: self.target,
             config: self.config.merge(&LintConfig {
                 allow: vec![],
@@ -395,6 +535,7 @@ impl Step for CI {
                 forbid: vec![],
             }),
         });
+
         let library_clippy_cfg = LintConfig {
             allow: vec!["clippy::all".into()],
             warn: vec![],
@@ -412,11 +553,13 @@ impl Step for CI {
             ],
             forbid: vec![],
         };
-        builder.ensure(Std {
-            target: self.target,
-            config: self.config.merge(&library_clippy_cfg),
-            crates: vec![],
-        });
+        builder.ensure(Std::from_build_compiler(
+            // This will be the stage 1 compiler, to avoid building rustc stage 2 just to lint std
+            builder.compiler(1, self.target),
+            self.target,
+            self.config.merge(&library_clippy_cfg),
+            vec![],
+        ));
 
         let compiler_clippy_cfg = LintConfig {
             allow: vec!["clippy::all".into()],
@@ -437,11 +580,13 @@ impl Step for CI {
             ],
             forbid: vec![],
         };
-        builder.ensure(Rustc {
-            target: self.target,
-            config: self.config.merge(&compiler_clippy_cfg),
-            crates: vec![],
-        });
+        // This will lint stage 2 rustc using stage 1 Clippy
+        builder.ensure(Rustc::new(
+            builder,
+            self.target,
+            self.config.merge(&compiler_clippy_cfg),
+            vec![],
+        ));
 
         let rustc_codegen_gcc = LintConfig {
             allow: vec![],
@@ -449,9 +594,11 @@ impl Step for CI {
             deny: vec!["warnings".into()],
             forbid: vec![],
         };
-        builder.ensure(CodegenGcc {
-            target: self.target,
-            config: self.config.merge(&rustc_codegen_gcc),
-        });
+        // This will check stage 2 rustc
+        builder.ensure(CodegenGcc::new(
+            builder,
+            self.target,
+            self.config.merge(&rustc_codegen_gcc),
+        ));
     }
 }
diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs
index de4b941ac90..54bf1842ab3 100644
--- a/src/bootstrap/src/core/builder/mod.rs
+++ b/src/bootstrap/src/core/builder/mod.rs
@@ -160,6 +160,10 @@ impl StepMetadata {
         Self::new(name, target, Kind::Check)
     }
 
+    pub fn clippy(name: &str, target: TargetSelection) -> Self {
+        Self::new(name, target, Kind::Clippy)
+    }
+
     pub fn doc(name: &str, target: TargetSelection) -> Self {
         Self::new(name, target, Kind::Doc)
     }
@@ -1552,35 +1556,6 @@ You have to build a stage1 compiler for `{}` first, and then use it to build a s
         self.ensure(tool::Rustdoc { target_compiler })
     }
 
-    pub fn cargo_clippy_cmd(&self, run_compiler: Compiler) -> BootstrapCommand {
-        if run_compiler.stage == 0 {
-            let cargo_clippy = self
-                .config
-                .initial_cargo_clippy
-                .clone()
-                .unwrap_or_else(|| self.build.config.download_clippy());
-
-            let mut cmd = command(cargo_clippy);
-            cmd.env("CARGO", &self.initial_cargo);
-            return cmd;
-        }
-
-        // FIXME: double check that `run_compiler`'s stage is what we want to use
-        let compilers =
-            RustcPrivateCompilers::new(self, run_compiler.stage, self.build.host_target);
-        assert_eq!(run_compiler, compilers.target_compiler());
-
-        let _ = self.ensure(tool::Clippy::from_compilers(compilers));
-        let cargo_clippy = self.ensure(tool::CargoClippy::from_compilers(compilers));
-        let mut dylib_path = helpers::dylib_path();
-        dylib_path.insert(0, self.sysroot(run_compiler).join("lib"));
-
-        let mut cmd = command(cargo_clippy.tool_path);
-        cmd.env(helpers::dylib_path_var(), env::join_paths(&dylib_path).unwrap());
-        cmd.env("CARGO", &self.initial_cargo);
-        cmd
-    }
-
     pub fn cargo_miri_cmd(&self, run_compiler: Compiler) -> BootstrapCommand {
         assert!(run_compiler.stage > 0, "miri can not be invoked at stage 0");
 
@@ -1607,6 +1582,37 @@ You have to build a stage1 compiler for `{}` first, and then use it to build a s
         cmd
     }
 
+    /// Create a Cargo command for running Clippy.
+    /// The used Clippy is (or in the case of stage 0, already was) built using `build_compiler`.
+    pub fn cargo_clippy_cmd(&self, build_compiler: Compiler) -> BootstrapCommand {
+        if build_compiler.stage == 0 {
+            let cargo_clippy = self
+                .config
+                .initial_cargo_clippy
+                .clone()
+                .unwrap_or_else(|| self.build.config.download_clippy());
+
+            let mut cmd = command(cargo_clippy);
+            cmd.env("CARGO", &self.initial_cargo);
+            return cmd;
+        }
+
+        // If we're linting something with build_compiler stage N, we want to build Clippy stage N
+        // and use that to lint it. That is why we use the `build_compiler` as the target compiler
+        // for RustcPrivateCompilers. We will use build compiler stage N-1 to build Clippy stage N.
+        let compilers = RustcPrivateCompilers::from_target_compiler(self, build_compiler);
+
+        let _ = self.ensure(tool::Clippy::from_compilers(compilers));
+        let cargo_clippy = self.ensure(tool::CargoClippy::from_compilers(compilers));
+        let mut dylib_path = helpers::dylib_path();
+        dylib_path.insert(0, self.sysroot(build_compiler).join("lib"));
+
+        let mut cmd = command(cargo_clippy.tool_path);
+        cmd.env(helpers::dylib_path_var(), env::join_paths(&dylib_path).unwrap());
+        cmd.env("CARGO", &self.initial_cargo);
+        cmd
+    }
+
     pub fn rustdoc_cmd(&self, compiler: Compiler) -> BootstrapCommand {
         let mut cmd = command(self.bootstrap_out.join("rustdoc"));
         cmd.env("RUSTC_STAGE", compiler.stage.to_string())
diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs
index 32d191c4265..9ba57542549 100644
--- a/src/bootstrap/src/core/builder/tests.rs
+++ b/src/bootstrap/src/core/builder/tests.rs
@@ -1515,6 +1515,7 @@ mod snapshot {
             ctx.config("check")
                 .path("compiler")
                 .render_steps(), @r"
+        [check] rustc 0 <host> -> rustc 1 <host> (73 crates)
         [check] rustc 0 <host> -> rustc 1 <host>
         [check] rustc 0 <host> -> rustc_codegen_cranelift 1 <host>
         [check] rustc 0 <host> -> rustc_codegen_gcc 1 <host>
@@ -1527,9 +1528,7 @@ mod snapshot {
         insta::assert_snapshot!(
             ctx.config("check")
                 .path("rustc")
-                .render_steps(), @r"
-        [check] rustc 0 <host> -> rustc 1 <host>
-        ");
+                .render_steps(), @"[check] rustc 0 <host> -> rustc 1 <host> (1 crates)");
     }
 
     #[test]
@@ -1547,6 +1546,7 @@ mod snapshot {
                 .path("compiler")
                 .stage(1)
                 .render_steps(), @r"
+        [check] rustc 0 <host> -> rustc 1 <host> (73 crates)
         [check] rustc 0 <host> -> rustc 1 <host>
         [check] rustc 0 <host> -> rustc_codegen_cranelift 1 <host>
         [check] rustc 0 <host> -> rustc_codegen_gcc 1 <host>
@@ -1564,6 +1564,7 @@ mod snapshot {
         [build] llvm <host>
         [build] rustc 0 <host> -> rustc 1 <host>
         [build] rustc 1 <host> -> std 1 <host>
+        [check] rustc 1 <host> -> rustc 2 <host> (73 crates)
         [check] rustc 1 <host> -> rustc 2 <host>
         [check] rustc 1 <host> -> rustc_codegen_cranelift 2 <host>
         [check] rustc 1 <host> -> rustc_codegen_gcc 2 <host>
@@ -1582,6 +1583,7 @@ mod snapshot {
         [build] rustc 0 <host> -> rustc 1 <host>
         [build] rustc 1 <host> -> std 1 <host>
         [build] rustc 1 <host> -> std 1 <target1>
+        [check] rustc 1 <host> -> rustc 2 <target1> (73 crates)
         [check] rustc 1 <host> -> rustc 2 <target1>
         [check] rustc 1 <host> -> Rustdoc 2 <target1>
         [check] rustc 1 <host> -> rustc_codegen_cranelift 2 <target1>
@@ -1678,6 +1680,7 @@ mod snapshot {
                 .paths(&["library", "compiler"])
                 .args(&args)
                 .render_steps(), @r"
+        [check] rustc 0 <host> -> rustc 1 <host> (73 crates)
         [check] rustc 0 <host> -> rustc 1 <host>
         [check] rustc 0 <host> -> rustc_codegen_cranelift 1 <host>
         [check] rustc 0 <host> -> rustc_codegen_gcc 1 <host>
@@ -2065,6 +2068,130 @@ mod snapshot {
         [doc] rustc 1 <host> -> reference (book) 2 <host>
         ");
     }
+
+    #[test]
+    fn clippy_ci() {
+        let ctx = TestCtx::new();
+        insta::assert_snapshot!(
+            ctx.config("clippy")
+                .path("ci")
+                .stage(2)
+                .render_steps(), @r"
+        [build] llvm <host>
+        [build] rustc 0 <host> -> rustc 1 <host>
+        [build] rustc 1 <host> -> std 1 <host>
+        [build] rustc 0 <host> -> clippy-driver 1 <host>
+        [build] rustc 0 <host> -> cargo-clippy 1 <host>
+        [clippy] rustc 1 <host> -> bootstrap 2 <host>
+        [clippy] rustc 1 <host> -> std 1 <host>
+        [clippy] rustc 1 <host> -> rustc 2 <host>
+        [check] rustc 1 <host> -> rustc 2 <host>
+        [clippy] rustc 1 <host> -> rustc_codegen_gcc 2 <host>
+        ");
+    }
+
+    #[test]
+    fn clippy_compiler_stage1() {
+        let ctx = TestCtx::new();
+        insta::assert_snapshot!(
+            ctx.config("clippy")
+                .path("compiler")
+                .render_steps(), @r"
+        [build] llvm <host>
+        [clippy] rustc 0 <host> -> rustc 1 <host>
+        ");
+    }
+
+    #[test]
+    fn clippy_compiler_stage2() {
+        let ctx = TestCtx::new();
+        insta::assert_snapshot!(
+            ctx.config("clippy")
+                .path("compiler")
+                .stage(2)
+                .render_steps(), @r"
+        [build] llvm <host>
+        [build] rustc 0 <host> -> rustc 1 <host>
+        [build] rustc 1 <host> -> std 1 <host>
+        [build] rustc 0 <host> -> clippy-driver 1 <host>
+        [build] rustc 0 <host> -> cargo-clippy 1 <host>
+        [clippy] rustc 1 <host> -> rustc 2 <host>
+        ");
+    }
+
+    #[test]
+    fn clippy_std_stage1() {
+        let ctx = TestCtx::new();
+        insta::assert_snapshot!(
+            ctx.config("clippy")
+                .path("std")
+                .render_steps(), @r"
+        [build] llvm <host>
+        [build] rustc 0 <host> -> rustc 1 <host>
+        [build] rustc 0 <host> -> clippy-driver 1 <host>
+        [build] rustc 0 <host> -> cargo-clippy 1 <host>
+        [clippy] rustc 1 <host> -> std 1 <host>
+        ");
+    }
+
+    #[test]
+    fn clippy_std_stage2() {
+        let ctx = TestCtx::new();
+        insta::assert_snapshot!(
+            ctx.config("clippy")
+                .path("std")
+                .stage(2)
+                .render_steps(), @r"
+        [build] llvm <host>
+        [build] rustc 0 <host> -> rustc 1 <host>
+        [build] rustc 1 <host> -> std 1 <host>
+        [build] rustc 1 <host> -> rustc 2 <host>
+        [build] rustc 1 <host> -> clippy-driver 2 <host>
+        [build] rustc 1 <host> -> cargo-clippy 2 <host>
+        [clippy] rustc 2 <host> -> std 2 <host>
+        ");
+    }
+
+    #[test]
+    fn clippy_miri_stage1() {
+        let ctx = TestCtx::new();
+        insta::assert_snapshot!(
+            ctx.config("clippy")
+                .path("miri")
+                .stage(1)
+                .render_steps(), @r"
+        [build] llvm <host>
+        [check] rustc 0 <host> -> rustc 1 <host>
+        [clippy] rustc 0 <host> -> miri 1 <host>
+        ");
+    }
+
+    #[test]
+    fn clippy_miri_stage2() {
+        let ctx = TestCtx::new();
+        insta::assert_snapshot!(
+            ctx.config("clippy")
+                .path("miri")
+                .stage(2)
+                .render_steps(), @r"
+        [build] llvm <host>
+        [build] rustc 0 <host> -> rustc 1 <host>
+        [build] rustc 1 <host> -> std 1 <host>
+        [check] rustc 1 <host> -> rustc 2 <host>
+        [build] rustc 0 <host> -> clippy-driver 1 <host>
+        [build] rustc 0 <host> -> cargo-clippy 1 <host>
+        [clippy] rustc 1 <host> -> miri 2 <host>
+        ");
+    }
+
+    #[test]
+    fn clippy_bootstrap() {
+        let ctx = TestCtx::new();
+        insta::assert_snapshot!(
+            ctx.config("clippy")
+                .path("bootstrap")
+                .render_steps(), @"[clippy] rustc 0 <host> -> bootstrap 1 <host>");
+    }
 }
 
 struct ExecutedSteps {
diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs
index a656927b1f6..9ced81e1e28 100644
--- a/src/bootstrap/src/core/config/config.rs
+++ b/src/bootstrap/src/core/config/config.rs
@@ -1375,6 +1375,10 @@ impl Config {
                 eprintln!("ERROR: cannot document anything on stage 0. Use at least stage 1.");
                 exit!(1);
             }
+            (0, Subcommand::Clippy { .. }) => {
+                eprintln!("ERROR: cannot run clippy on stage 0. Use at least stage 1.");
+                exit!(1);
+            }
             _ => {}
         }
 
diff --git a/src/bootstrap/src/utils/build_stamp.rs b/src/bootstrap/src/utils/build_stamp.rs
index bd4eb790ae5..6c79385190e 100644
--- a/src/bootstrap/src/utils/build_stamp.rs
+++ b/src/bootstrap/src/utils/build_stamp.rs
@@ -146,13 +146,13 @@ pub fn libstd_stamp(
 }
 
 /// Cargo's output path for librustc in a given stage, compiled by a particular
-/// compiler for the specified target.
+/// `build_compiler` for the specified target.
 pub fn librustc_stamp(
     builder: &Builder<'_>,
-    compiler: Compiler,
+    build_compiler: Compiler,
     target: TargetSelection,
 ) -> BuildStamp {
-    BuildStamp::new(&builder.cargo_out(compiler, Mode::Rustc, target)).with_prefix("librustc")
+    BuildStamp::new(&builder.cargo_out(build_compiler, Mode::Rustc, target)).with_prefix("librustc")
 }
 
 /// Computes a hash representing the state of a repository/submodule and additional input.
diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs
index cd7fba39a84..b454a8ddefb 100644
--- a/src/bootstrap/src/utils/change_tracker.rs
+++ b/src/bootstrap/src/utils/change_tracker.rs
@@ -501,4 +501,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[
         severity: ChangeSeverity::Warning,
         summary: "The names of stageN directories in the build directory have been consolidated with the new (post-stage-0-redesign) staging scheme. Some tools and binaries might be located in a different build directory than before.",
     },
+    ChangeInfo {
+        change_id: 145131,
+        severity: ChangeSeverity::Warning,
+        summary: "It is no longer possible to `x clippy` with stage 0. All clippy commands have to be on stage 1+.",
+    },
 ];
diff --git a/src/ci/docker/host-x86_64/pr-check-2/Dockerfile b/src/ci/docker/host-x86_64/pr-check-2/Dockerfile
index 6fea2437276..8073b8efb46 100644
--- a/src/ci/docker/host-x86_64/pr-check-2/Dockerfile
+++ b/src/ci/docker/host-x86_64/pr-check-2/Dockerfile
@@ -28,7 +28,7 @@ RUN sh /scripts/sccache.sh
 
 ENV SCRIPT \
         python3 ../x.py check && \
-        python3 ../x.py clippy ci && \
+        python3 ../x.py clippy ci --stage 2 && \
         python3 ../x.py test --stage 1 core alloc std test proc_macro && \
         python3 ../x.py test --stage 1 src/tools/compiletest && \
         python3 ../x.py doc bootstrap && \
diff --git a/src/tools/clippy/tests/ui/crashes/ice-6255.rs b/src/tools/clippy/tests/ui/crashes/ice-6255.rs
index ef1e01f80ef..5f31696c791 100644
--- a/src/tools/clippy/tests/ui/crashes/ice-6255.rs
+++ b/src/tools/clippy/tests/ui/crashes/ice-6255.rs
@@ -9,7 +9,7 @@ macro_rules! define_other_core {
 }
 
 fn main() {
-    core::panic!();
+    core::panic!(); //~ ERROR: `core` is ambiguous
 }
 
 define_other_core!();
diff --git a/src/tools/clippy/tests/ui/crashes/ice-6255.stderr b/src/tools/clippy/tests/ui/crashes/ice-6255.stderr
index 738e9d1bd5c..420e4af936f 100644
--- a/src/tools/clippy/tests/ui/crashes/ice-6255.stderr
+++ b/src/tools/clippy/tests/ui/crashes/ice-6255.stderr
@@ -9,5 +9,25 @@ LL | define_other_core!();
    |
    = note: this error originates in the macro `define_other_core` (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error: aborting due to 1 previous error
+error[E0659]: `core` is ambiguous
+  --> tests/ui/crashes/ice-6255.rs:12:5
+   |
+LL |     core::panic!();
+   |     ^^^^ ambiguous name
+   |
+   = note: ambiguous because of a conflict between a macro-expanded name and a less macro-expanded name from outer scope during import or macro resolution
+   = note: `core` could refer to a built-in crate
+note: `core` could also refer to the crate imported here
+  --> tests/ui/crashes/ice-6255.rs:6:9
+   |
+LL |         extern crate std as core;
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | define_other_core!();
+   | -------------------- in this macro invocation
+   = help: use `crate::core` to refer to this crate unambiguously
+   = note: this error originates in the macro `define_other_core` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 2 previous errors
 
+For more information about this error, try `rustc --explain E0659`.
diff --git a/tests/ui/imports/issue-109148.rs b/tests/ui/imports/issue-109148.rs
index 9d657a87381..49fc2fe0f5b 100644
--- a/tests/ui/imports/issue-109148.rs
+++ b/tests/ui/imports/issue-109148.rs
@@ -10,6 +10,7 @@ macro_rules! m {
 
 m!();
 
-use std::mem;
+use std::mem; //~ ERROR `std` is ambiguous
+use ::std::mem as _; //~ ERROR `std` is ambiguous
 
 fn main() {}
diff --git a/tests/ui/imports/issue-109148.stderr b/tests/ui/imports/issue-109148.stderr
index b7f1f69dc8f..ee047385ae3 100644
--- a/tests/ui/imports/issue-109148.stderr
+++ b/tests/ui/imports/issue-109148.stderr
@@ -9,5 +9,43 @@ LL | m!();
    |
    = note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error: aborting due to 1 previous error
+error[E0659]: `std` is ambiguous
+  --> $DIR/issue-109148.rs:13:5
+   |
+LL | use std::mem;
+   |     ^^^ ambiguous name
+   |
+   = note: ambiguous because of a conflict between a macro-expanded name and a less macro-expanded name from outer scope during import or macro resolution
+   = note: `std` could refer to a built-in crate
+note: `std` could also refer to the crate imported here
+  --> $DIR/issue-109148.rs:6:9
+   |
+LL |         extern crate core as std;
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | m!();
+   | ---- in this macro invocation
+   = help: use `crate::std` to refer to this crate unambiguously
+   = note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error[E0659]: `std` is ambiguous
+  --> $DIR/issue-109148.rs:14:7
+   |
+LL | use ::std::mem as _;
+   |       ^^^ ambiguous name
+   |
+   = note: ambiguous because of a conflict between a macro-expanded name and a less macro-expanded name from outer scope during import or macro resolution
+   = note: `std` could refer to a built-in crate
+note: `std` could also refer to the crate imported here
+  --> $DIR/issue-109148.rs:6:9
+   |
+LL |         extern crate core as std;
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | m!();
+   | ---- in this macro invocation
+   = note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 3 previous errors
 
+For more information about this error, try `rustc --explain E0659`.
diff --git a/tests/ui/macros/issue-78325-inconsistent-resolution.rs b/tests/ui/macros/issue-78325-inconsistent-resolution.rs
index 919eca4f9bf..021ba599d12 100644
--- a/tests/ui/macros/issue-78325-inconsistent-resolution.rs
+++ b/tests/ui/macros/issue-78325-inconsistent-resolution.rs
@@ -1,3 +1,5 @@
+//@ edition: 2018
+
 macro_rules! define_other_core {
     ( ) => {
         extern crate std as core;
@@ -6,7 +8,8 @@ macro_rules! define_other_core {
 }
 
 fn main() {
-    core::panic!();
+    core::panic!(); //~ ERROR `core` is ambiguous
+    ::core::panic!(); //~ ERROR `core` is ambiguous
 }
 
 define_other_core!();
diff --git a/tests/ui/macros/issue-78325-inconsistent-resolution.stderr b/tests/ui/macros/issue-78325-inconsistent-resolution.stderr
index b75e4a9c9e0..7c745040640 100644
--- a/tests/ui/macros/issue-78325-inconsistent-resolution.stderr
+++ b/tests/ui/macros/issue-78325-inconsistent-resolution.stderr
@@ -1,5 +1,5 @@
 error: macro-expanded `extern crate` items cannot shadow names passed with `--extern`
-  --> $DIR/issue-78325-inconsistent-resolution.rs:3:9
+  --> $DIR/issue-78325-inconsistent-resolution.rs:5:9
    |
 LL |         extern crate std as core;
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -9,5 +9,43 @@ LL | define_other_core!();
    |
    = note: this error originates in the macro `define_other_core` (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error: aborting due to 1 previous error
+error[E0659]: `core` is ambiguous
+  --> $DIR/issue-78325-inconsistent-resolution.rs:11:5
+   |
+LL |     core::panic!();
+   |     ^^^^ ambiguous name
+   |
+   = note: ambiguous because of a conflict between a macro-expanded name and a less macro-expanded name from outer scope during import or macro resolution
+   = note: `core` could refer to a built-in crate
+note: `core` could also refer to the crate imported here
+  --> $DIR/issue-78325-inconsistent-resolution.rs:5:9
+   |
+LL |         extern crate std as core;
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | define_other_core!();
+   | -------------------- in this macro invocation
+   = help: use `crate::core` to refer to this crate unambiguously
+   = note: this error originates in the macro `define_other_core` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error[E0659]: `core` is ambiguous
+  --> $DIR/issue-78325-inconsistent-resolution.rs:12:7
+   |
+LL |     ::core::panic!();
+   |       ^^^^ ambiguous name
+   |
+   = note: ambiguous because of a conflict between a macro-expanded name and a less macro-expanded name from outer scope during import or macro resolution
+   = note: `core` could refer to a built-in crate
+note: `core` could also refer to the crate imported here
+  --> $DIR/issue-78325-inconsistent-resolution.rs:5:9
+   |
+LL |         extern crate std as core;
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | define_other_core!();
+   | -------------------- in this macro invocation
+   = note: this error originates in the macro `define_other_core` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 3 previous errors
 
+For more information about this error, try `rustc --explain E0659`.
diff --git a/tests/ui/resolve/extern-prelude-speculative.rs b/tests/ui/resolve/extern-prelude-speculative.rs
new file mode 100644
index 00000000000..afbc32d22ac
--- /dev/null
+++ b/tests/ui/resolve/extern-prelude-speculative.rs
@@ -0,0 +1,10 @@
+// Non-existent path in `--extern` doesn't result in an error if it's shadowed by `extern crate`.
+
+//@ check-pass
+//@ compile-flags: --extern something=/path/to/nowhere
+
+extern crate std as something;
+
+fn main() {
+    something::println!();
+}
diff --git a/tests/ui/resolve/visibility-indeterminate.rs b/tests/ui/resolve/visibility-indeterminate.rs
index 17e5fec4701..181bb290774 100644
--- a/tests/ui/resolve/visibility-indeterminate.rs
+++ b/tests/ui/resolve/visibility-indeterminate.rs
@@ -2,6 +2,6 @@
 
 foo!(); //~ ERROR cannot find macro `foo` in this scope
 
-pub(in ::bar) struct Baz {} //~ ERROR cannot determine resolution for the visibility
+pub(in ::bar) struct Baz {} //~ ERROR failed to resolve: could not find `bar` in the list of imported crates
 
 fn main() {}
diff --git a/tests/ui/resolve/visibility-indeterminate.stderr b/tests/ui/resolve/visibility-indeterminate.stderr
index 84d82ce8522..bbe28747f7c 100644
--- a/tests/ui/resolve/visibility-indeterminate.stderr
+++ b/tests/ui/resolve/visibility-indeterminate.stderr
@@ -1,8 +1,8 @@
-error[E0578]: cannot determine resolution for the visibility
-  --> $DIR/visibility-indeterminate.rs:5:8
+error[E0433]: failed to resolve: could not find `bar` in the list of imported crates
+  --> $DIR/visibility-indeterminate.rs:5:10
    |
 LL | pub(in ::bar) struct Baz {}
-   |        ^^^^^
+   |          ^^^ could not find `bar` in the list of imported crates
 
 error: cannot find macro `foo` in this scope
   --> $DIR/visibility-indeterminate.rs:3:1
@@ -12,4 +12,4 @@ LL | foo!();
 
 error: aborting due to 2 previous errors
 
-For more information about this error, try `rustc --explain E0578`.
+For more information about this error, try `rustc --explain E0433`.
diff --git a/tests/ui/rust-2018/uniform-paths/deadlock.rs b/tests/ui/rust-2018/uniform-paths/deadlock.rs
index 4011ba3ee28..d2296c51bdd 100644
--- a/tests/ui/rust-2018/uniform-paths/deadlock.rs
+++ b/tests/ui/rust-2018/uniform-paths/deadlock.rs
@@ -2,7 +2,7 @@
 //@ compile-flags:--extern foo --extern bar
 
 use bar::foo; //~ ERROR can't find crate for `bar`
-use foo::bar; //~ ERROR can't find crate for `foo`
+use foo::bar;
 //~^^ ERROR unresolved imports `bar::foo`, `foo::bar`
 
 fn main() {}
diff --git a/tests/ui/rust-2018/uniform-paths/deadlock.stderr b/tests/ui/rust-2018/uniform-paths/deadlock.stderr
index 8b9863948bd..c50bc16ac55 100644
--- a/tests/ui/rust-2018/uniform-paths/deadlock.stderr
+++ b/tests/ui/rust-2018/uniform-paths/deadlock.stderr
@@ -4,12 +4,6 @@ error[E0463]: can't find crate for `bar`
 LL | use bar::foo;
    |     ^^^ can't find crate
 
-error[E0463]: can't find crate for `foo`
-  --> $DIR/deadlock.rs:5:5
-   |
-LL | use foo::bar;
-   |     ^^^ can't find crate
-
 error[E0432]: unresolved imports `bar::foo`, `foo::bar`
   --> $DIR/deadlock.rs:4:5
    |
@@ -18,7 +12,7 @@ LL | use bar::foo;
 LL | use foo::bar;
    |     ^^^^^^^^
 
-error: aborting due to 3 previous errors
+error: aborting due to 2 previous errors
 
 Some errors have detailed explanations: E0432, E0463.
 For more information about an error, try `rustc --explain E0432`.