From 6e7e243ef2790169cfd0e228d22fb6fdd9093317 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Fri, 8 Aug 2025 16:30:43 +0200 Subject: [PATCH 01/15] Prepare standard library for checking rustc in `prepare_compiler_for_check` --- src/bootstrap/src/core/build_steps/check.rs | 48 +++++++++----------- src/bootstrap/src/core/build_steps/clippy.rs | 2 +- 2 files changed, 23 insertions(+), 27 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/check.rs b/src/bootstrap/src/core/build_steps/check.rs index 0cbf8f55e9933..2d27ad4cdf325 100644 --- a/src/bootstrap/src/core/build_steps/check.rs +++ b/src/bootstrap/src/core/build_steps/check.rs @@ -168,12 +168,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) -> Self { + let build_compiler = prepare_compiler_for_check(builder, target, Mode::Rustc); Self { build_compiler, target, crates } } } @@ -189,11 +185,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 +199,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, @@ -289,11 +272,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 +290,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 4d734fe5c667d..2cba9aa8e8b59 100644 --- a/src/bootstrap/src/core/build_steps/clippy.rs +++ b/src/bootstrap/src/core/build_steps/clippy.rs @@ -298,7 +298,7 @@ macro_rules! lint_any { let target = self.target; if !builder.download_rustc() { - builder.ensure(check::Rustc::new(builder, build_compiler, target)); + builder.ensure(check::Rustc::new(builder, target, vec![])); }; let cargo = prepare_tool_cargo( From 5d1535ca294db11c1cfff39aa31e18164eae28b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Fri, 8 Aug 2025 16:59:03 +0200 Subject: [PATCH 02/15] Implement `clippy::CodegenGcc` as a separate step To correctly pass `RustcPrivateCompilers` to it and to avoid running it on `x clippy compiler`. --- src/bootstrap/src/core/build_steps/clippy.rs | 84 ++++++++++++++++++-- src/bootstrap/src/core/builder/mod.rs | 4 + 2 files changed, 81 insertions(+), 7 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/clippy.rs b/src/bootstrap/src/core/build_steps/clippy.rs index 2cba9aa8e8b59..7ec6649aefbad 100644 --- a/src/bootstrap/src/core/build_steps/clippy.rs +++ b/src/bootstrap/src/core/build_steps/clippy.rs @@ -2,11 +2,11 @@ use super::check; use super::compile::{run_cargo, rustc_cargo, std_cargo}; -use super::tool::{SourceType, prepare_tool_cargo}; +use super::tool::{RustcPrivateCompilers, SourceType, prepare_tool_cargo}; use crate::builder::{Builder, ShouldRun}; 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}; @@ -263,6 +263,76 @@ impl Step for Rustc { } } +#[derive(Debug, Clone, Hash, PartialEq, Eq)] +pub struct CodegenGcc { + compilers: RustcPrivateCompilers, + target: TargetSelection, + config: LintConfig, +} + +impl CodegenGcc { + fn new(builder: &Builder<'_>, target: TargetSelection, config: LintConfig) -> Self { + Self { + compilers: RustcPrivateCompilers::new(builder, builder.top_stage, target), + 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.compilers.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 { + Some( + StepMetadata::clippy("rustc_codegen_gcc", self.target) + .built_by(self.compilers.build_compiler()), + ) + } +} + macro_rules! lint_any { ($( $name:ident, $path:expr, $readable_name:expr @@ -346,7 +416,6 @@ lint_any!( 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"; @@ -449,9 +518,10 @@ impl Step for CI { deny: vec!["warnings".into()], forbid: vec![], }; - builder.ensure(CodegenGcc { - target: self.target, - config: self.config.merge(&rustc_codegen_gcc), - }); + 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 de4b941ac9082..8e9f3c1eab469 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) } From d2165674e78aff1d0712b02bf33979230c321c85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Fri, 8 Aug 2025 17:00:24 +0200 Subject: [PATCH 03/15] Forbid running Clippy on stage 0 --- src/bootstrap/src/core/config/config.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index a656927b1f64d..9ced81e1e283d 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); + } _ => {} } From ba27938c8ca9a6b7e97a1011d611dcfcab78b550 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Fri, 8 Aug 2025 17:06:40 +0200 Subject: [PATCH 04/15] Add snapshot test for `x clippy ci` --- src/bootstrap/src/core/build_steps/clippy.rs | 12 ++++++ src/bootstrap/src/core/builder/tests.rs | 45 ++++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/src/bootstrap/src/core/build_steps/clippy.rs b/src/bootstrap/src/core/build_steps/clippy.rs index 7ec6649aefbad..f029f9fa9390a 100644 --- a/src/bootstrap/src/core/build_steps/clippy.rs +++ b/src/bootstrap/src/core/build_steps/clippy.rs @@ -178,6 +178,10 @@ impl Step for Std { false, ); } + + fn metadata(&self) -> Option { + Some(StepMetadata::clippy("std", self.target)) + } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -261,6 +265,10 @@ impl Step for Rustc { false, ); } + + fn metadata(&self) -> Option { + Some(StepMetadata::clippy("rustc", self.target)) + } } #[derive(Debug, Clone, Hash, PartialEq, Eq)] @@ -404,6 +412,10 @@ macro_rules! lint_any { false, ); } + + fn metadata(&self) -> Option { + Some(StepMetadata::clippy($readable_name, self.target)) + } } )+ } diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 32d191c4265de..b9fc3ed57ec27 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -2065,6 +2065,51 @@ mod snapshot { [doc] rustc 1 -> reference (book) 2 "); } + + #[test] + fn clippy_ci() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx.config("clippy") + .path("ci") + .render_steps(), @r" + [build] llvm + [build] rustc 0 -> rustc 1 + [check] rustc 1 -> rustc 2 + [build] rustc 0 -> clippy-driver 1 + [build] rustc 0 -> cargo-clippy 1 + [clippy] bootstrap + [clippy] std + [build] rustc 1 -> std 1 + [clippy] rustc + [clippy] rustc 0 -> rustc_codegen_gcc 1 + "); + } + + #[test] + fn clippy_ci_stage_2() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx.config("clippy") + .path("ci") + .stage(2) + .render_steps(), @r" + [build] llvm + [build] rustc 0 -> rustc 1 + [build] rustc 1 -> std 1 + [build] rustc 1 -> rustc 2 + [check] rustc 2 -> rustc 3 + [build] rustc 1 -> clippy-driver 2 + [build] rustc 1 -> cargo-clippy 2 + [clippy] bootstrap + [clippy] std + [build] rustc 2 -> std 2 + [clippy] rustc + [build] rustc 0 -> clippy-driver 1 + [build] rustc 0 -> cargo-clippy 1 + [clippy] rustc 1 -> rustc_codegen_gcc 2 + "); + } } struct ExecutedSteps { From 5ffd5c2ec23f823a6dcb7c67d9d19b0723777fda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Fri, 8 Aug 2025 17:09:58 +0200 Subject: [PATCH 05/15] Fix Clippy staging for compiler --- src/bootstrap/src/core/build_steps/check.rs | 6 +- src/bootstrap/src/core/build_steps/clippy.rs | 74 ++++++++++++-------- src/bootstrap/src/core/builder/mod.rs | 17 +++-- src/bootstrap/src/core/builder/tests.rs | 34 ++++++--- src/bootstrap/src/utils/build_stamp.rs | 6 +- 5 files changed, 84 insertions(+), 53 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/check.rs b/src/bootstrap/src/core/build_steps/check.rs index 2d27ad4cdf325..ae258bf2939ca 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 { @@ -241,7 +237,7 @@ impl Step for Rustc { } /// 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, diff --git a/src/bootstrap/src/core/build_steps/clippy.rs b/src/bootstrap/src/core/build_steps/clippy.rs index f029f9fa9390a..364d9bed883bd 100644 --- a/src/bootstrap/src/core/build_steps/clippy.rs +++ b/src/bootstrap/src/core/build_steps/clippy.rs @@ -1,14 +1,27 @@ //! 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 +//! `check::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 super::check; use super::compile::{run_cargo, rustc_cargo, std_cargo}; use super::tool::{RustcPrivateCompilers, 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, 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] = &[ @@ -184,14 +197,35 @@ impl Step for Std { } } +/// 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, } +impl Rustc { + fn new( + builder: &Builder<'_>, + target: TargetSelection, + config: LintConfig, + crates: Vec, + ) -> 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; @@ -202,33 +236,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, @@ -267,7 +284,7 @@ impl Step for Rustc { } fn metadata(&self) -> Option { - Some(StepMetadata::clippy("rustc", self.target)) + Some(StepMetadata::clippy("rustc", self.target).built_by(self.build_compiler)) } } @@ -518,11 +535,12 @@ impl Step for CI { ], forbid: vec![], }; - builder.ensure(Rustc { - target: self.target, - config: self.config.merge(&compiler_clippy_cfg), - crates: vec![], - }); + builder.ensure(Rustc::new( + builder, + self.target, + self.config.merge(&compiler_clippy_cfg), + vec![], + )); let rustc_codegen_gcc = LintConfig { allow: vec![], diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index 8e9f3c1eab469..9719bcbc56328 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -1556,8 +1556,10 @@ 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 { + /// 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 @@ -1569,15 +1571,16 @@ You have to build a stage1 compiler for `{}` first, and then use it to build a s 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 compilers = RustcPrivateCompilers::from_build_compiler( + self, + build_compiler, + self.build.host_target, + ); 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")); + 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()); diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index b9fc3ed57ec27..2c1623f5385be 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -2076,12 +2076,13 @@ mod snapshot { [build] llvm [build] rustc 0 -> rustc 1 [check] rustc 1 -> rustc 2 - [build] rustc 0 -> clippy-driver 1 - [build] rustc 0 -> cargo-clippy 1 + [build] rustc 1 -> std 1 + [build] rustc 1 -> rustc 2 + [build] rustc 1 -> clippy-driver 2 + [build] rustc 1 -> cargo-clippy 2 [clippy] bootstrap [clippy] std - [build] rustc 1 -> std 1 - [clippy] rustc + [clippy] rustc 0 -> rustc 1 [clippy] rustc 0 -> rustc_codegen_gcc 1 "); } @@ -2099,17 +2100,30 @@ mod snapshot { [build] rustc 1 -> std 1 [build] rustc 1 -> rustc 2 [check] rustc 2 -> rustc 3 - [build] rustc 1 -> clippy-driver 2 - [build] rustc 1 -> cargo-clippy 2 + [build] rustc 2 -> std 2 + [build] rustc 2 -> rustc 3 + [build] rustc 2 -> clippy-driver 3 + [build] rustc 2 -> cargo-clippy 3 [clippy] bootstrap [clippy] std - [build] rustc 2 -> std 2 - [clippy] rustc - [build] rustc 0 -> clippy-driver 1 - [build] rustc 0 -> cargo-clippy 1 + [build] rustc 1 -> clippy-driver 2 + [build] rustc 1 -> cargo-clippy 2 + [clippy] rustc 1 -> rustc 2 [clippy] rustc 1 -> rustc_codegen_gcc 2 "); } + + #[test] + fn clippy_compiler() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx.config("clippy") + .path("compiler") + .render_steps(), @r" + [build] llvm + [clippy] rustc 0 -> rustc 1 + "); + } } struct ExecutedSteps { diff --git a/src/bootstrap/src/utils/build_stamp.rs b/src/bootstrap/src/utils/build_stamp.rs index bd4eb790ae50e..6c79385190e8e 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. From 2071a68bd6e81a8e6779ee247048d37cd865a8c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Fri, 8 Aug 2025 17:19:36 +0200 Subject: [PATCH 06/15] Add snapshot test for `x clippy std` --- src/bootstrap/src/core/builder/tests.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 2c1623f5385be..c521b7f5d0747 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -2124,6 +2124,23 @@ mod snapshot { [clippy] rustc 0 -> rustc 1 "); } + + #[test] + fn clippy_std() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx.config("clippy") + .path("std") + .render_steps(), @r" + [build] llvm + [build] rustc 0 -> rustc 1 + [build] rustc 1 -> std 1 + [build] rustc 1 -> rustc 2 + [build] rustc 1 -> clippy-driver 2 + [build] rustc 1 -> cargo-clippy 2 + [clippy] std + "); + } } struct ExecutedSteps { From c5dac1aa2a48ac014c8a33a5c8c8334ed48afafa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Fri, 8 Aug 2025 17:23:34 +0200 Subject: [PATCH 07/15] Fix staging for `x clippy std` --- src/bootstrap/src/core/build_steps/clippy.rs | 36 +++++++++--- src/bootstrap/src/core/builder/mod.rs | 61 ++++++++++---------- src/bootstrap/src/core/builder/tests.rs | 26 ++++----- 3 files changed, 67 insertions(+), 56 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/clippy.rs b/src/bootstrap/src/core/build_steps/clippy.rs index 364d9bed883bd..f67cf0edf02f9 100644 --- a/src/bootstrap/src/core/build_steps/clippy.rs +++ b/src/bootstrap/src/core/build_steps/clippy.rs @@ -134,12 +134,29 @@ 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, } +impl Std { + fn new( + builder: &Builder<'_>, + target: TargetSelection, + config: LintConfig, + crates: Vec, + ) -> Self { + Self { + build_compiler: builder.compiler(builder.top_stage, builder.host_target), + target, + config, + crates, + } + } +} + impl Step for Std { type Output = (); const DEFAULT: bool = true; @@ -151,12 +168,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, @@ -193,7 +210,7 @@ impl Step for Std { } fn metadata(&self) -> Option { - Some(StepMetadata::clippy("std", self.target)) + Some(StepMetadata::clippy("std", self.target).built_by(self.build_compiler)) } } @@ -510,11 +527,12 @@ impl Step for CI { ], forbid: vec![], }; - builder.ensure(Std { - target: self.target, - config: self.config.merge(&library_clippy_cfg), - crates: vec![], - }); + builder.ensure(Std::new( + builder, + self.target, + self.config.merge(&library_clippy_cfg), + vec![], + )); let compiler_clippy_cfg = LintConfig { allow: vec!["clippy::all".into()], diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index 9719bcbc56328..54bf1842ab31b 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -1556,6 +1556,32 @@ 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_miri_cmd(&self, run_compiler: Compiler) -> BootstrapCommand { + assert!(run_compiler.stage > 0, "miri can not be invoked at stage 0"); + + let compilers = + RustcPrivateCompilers::new(self, run_compiler.stage, self.build.host_target); + assert_eq!(run_compiler, compilers.target_compiler()); + + // Prepare the tools + let miri = self.ensure(tool::Miri::from_compilers(compilers)); + let cargo_miri = self.ensure(tool::CargoMiri::from_compilers(compilers)); + // Invoke cargo-miri, make sure it can find miri and cargo. + let mut cmd = command(cargo_miri.tool_path); + cmd.env("MIRI", &miri.tool_path); + cmd.env("CARGO", &self.initial_cargo); + // Need to add the `run_compiler` libs. Those are the libs produces *by* `build_compiler` + // in `tool::ToolBuild` step, so they match the Miri we just built. However this means they + // are actually living one stage up, i.e. we are running `stage1-tools-bin/miri` with the + // libraries in `stage1/lib`. This is an unfortunate off-by-1 caused (possibly) by the fact + // that Miri doesn't have an "assemble" step like rustc does that would cross the stage boundary. + // We can't use `add_rustc_lib_path` as that's a NOP on Windows but we do need these libraries + // added to the PATH due to the stage mismatch. + // Also see https://github.com/rust-lang/rust/pull/123192#issuecomment-2028901503. + add_dylib_path(self.rustc_lib_paths(run_compiler), &mut cmd); + 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 { @@ -1571,11 +1597,10 @@ You have to build a stage1 compiler for `{}` first, and then use it to build a s return cmd; } - let compilers = RustcPrivateCompilers::from_build_compiler( - self, - build_compiler, - self.build.host_target, - ); + // 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)); @@ -1588,32 +1613,6 @@ You have to build a stage1 compiler for `{}` first, and then use it to build a s cmd } - pub fn cargo_miri_cmd(&self, run_compiler: Compiler) -> BootstrapCommand { - assert!(run_compiler.stage > 0, "miri can not be invoked at stage 0"); - - let compilers = - RustcPrivateCompilers::new(self, run_compiler.stage, self.build.host_target); - assert_eq!(run_compiler, compilers.target_compiler()); - - // Prepare the tools - let miri = self.ensure(tool::Miri::from_compilers(compilers)); - let cargo_miri = self.ensure(tool::CargoMiri::from_compilers(compilers)); - // Invoke cargo-miri, make sure it can find miri and cargo. - let mut cmd = command(cargo_miri.tool_path); - cmd.env("MIRI", &miri.tool_path); - cmd.env("CARGO", &self.initial_cargo); - // Need to add the `run_compiler` libs. Those are the libs produces *by* `build_compiler` - // in `tool::ToolBuild` step, so they match the Miri we just built. However this means they - // are actually living one stage up, i.e. we are running `stage1-tools-bin/miri` with the - // libraries in `stage1/lib`. This is an unfortunate off-by-1 caused (possibly) by the fact - // that Miri doesn't have an "assemble" step like rustc does that would cross the stage boundary. - // We can't use `add_rustc_lib_path` as that's a NOP on Windows but we do need these libraries - // added to the PATH due to the stage mismatch. - // Also see https://github.com/rust-lang/rust/pull/123192#issuecomment-2028901503. - add_dylib_path(self.rustc_lib_paths(run_compiler), &mut cmd); - 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 c521b7f5d0747..28bec10f06191 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -2076,12 +2076,10 @@ mod snapshot { [build] llvm [build] rustc 0 -> rustc 1 [check] rustc 1 -> rustc 2 - [build] rustc 1 -> std 1 - [build] rustc 1 -> rustc 2 - [build] rustc 1 -> clippy-driver 2 - [build] rustc 1 -> cargo-clippy 2 + [build] rustc 0 -> clippy-driver 1 + [build] rustc 0 -> cargo-clippy 1 [clippy] bootstrap - [clippy] std + [clippy] rustc 1 -> std 1 [clippy] rustc 0 -> rustc 1 [clippy] rustc 0 -> rustc_codegen_gcc 1 "); @@ -2100,14 +2098,12 @@ mod snapshot { [build] rustc 1 -> std 1 [build] rustc 1 -> rustc 2 [check] rustc 2 -> rustc 3 - [build] rustc 2 -> std 2 - [build] rustc 2 -> rustc 3 - [build] rustc 2 -> clippy-driver 3 - [build] rustc 2 -> cargo-clippy 3 - [clippy] bootstrap - [clippy] std [build] rustc 1 -> clippy-driver 2 [build] rustc 1 -> cargo-clippy 2 + [clippy] bootstrap + [clippy] rustc 2 -> std 2 + [build] rustc 0 -> clippy-driver 1 + [build] rustc 0 -> cargo-clippy 1 [clippy] rustc 1 -> rustc 2 [clippy] rustc 1 -> rustc_codegen_gcc 2 "); @@ -2134,11 +2130,9 @@ mod snapshot { .render_steps(), @r" [build] llvm [build] rustc 0 -> rustc 1 - [build] rustc 1 -> std 1 - [build] rustc 1 -> rustc 2 - [build] rustc 1 -> clippy-driver 2 - [build] rustc 1 -> cargo-clippy 2 - [clippy] std + [build] rustc 0 -> clippy-driver 1 + [build] rustc 0 -> cargo-clippy 1 + [clippy] rustc 1 -> std 1 "); } } From 87d92eecf925e990c6db13a8c2a56e36d4905ea9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Fri, 8 Aug 2025 21:29:30 +0200 Subject: [PATCH 08/15] Fix staging of linting tools --- src/bootstrap/src/core/build_steps/clippy.rs | 74 ++++++++++---------- 1 file changed, 38 insertions(+), 36 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/clippy.rs b/src/bootstrap/src/core/build_steps/clippy.rs index f67cf0edf02f9..d8cf3d0c48856 100644 --- a/src/bootstrap/src/core/build_steps/clippy.rs +++ b/src/bootstrap/src/core/build_steps/clippy.rs @@ -6,13 +6,12 @@ //! which is performed by the `x clippy ci` command. //! //! In order to prepare a build compiler for running clippy, use the -//! `check::prepare_compiler_for_check` function. That prepares a compiler and a standard library +//! [check::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 super::check; use super::compile::{run_cargo, rustc_cargo, std_cargo}; use super::tool::{RustcPrivateCompilers, SourceType, prepare_tool_cargo}; use crate::builder::{Builder, ShouldRun}; @@ -377,7 +376,10 @@ impl Step for CodegenGcc { 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)* ; )+) => { @@ -385,7 +387,8 @@ macro_rules! lint_any { #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct $name { - pub target: TargetSelection, + build_compiler: Compiler, + target: TargetSelection, config: LintConfig, } @@ -400,23 +403,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, target, vec![])); - }; - let cargo = prepare_tool_cargo( builder, build_compiler, - Mode::ToolRustc, + $mode, target, Kind::Clippy, $path, @@ -427,13 +426,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( @@ -455,30 +454,32 @@ macro_rules! lint_any { } } +// 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"; - 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; ); #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -502,6 +503,7 @@ impl Step for CI { fn run(self, builder: &Builder<'_>) -> Self::Output { builder.ensure(Bootstrap { + build_compiler: prepare_compiler_for_check(builder, self.target, Mode::ToolTarget), target: self.target, config: self.config.merge(&LintConfig { allow: vec![], From 23bb607184e51870b278de3bcd2e8786c54faedf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Fri, 8 Aug 2025 21:43:05 +0200 Subject: [PATCH 09/15] Add more tests --- src/bootstrap/src/core/build_steps/clippy.rs | 2 +- src/bootstrap/src/core/builder/tests.rs | 90 ++++++++++++++++++-- 2 files changed, 83 insertions(+), 9 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/clippy.rs b/src/bootstrap/src/core/build_steps/clippy.rs index d8cf3d0c48856..b71e5fbb95ca4 100644 --- a/src/bootstrap/src/core/build_steps/clippy.rs +++ b/src/bootstrap/src/core/build_steps/clippy.rs @@ -447,7 +447,7 @@ macro_rules! lint_any { } fn metadata(&self) -> Option { - Some(StepMetadata::clippy($readable_name, self.target)) + Some(StepMetadata::clippy($readable_name, self.target).built_by(self.build_compiler)) } } )+ diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 28bec10f06191..7fdbdd05fb683 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -2073,12 +2073,11 @@ mod snapshot { ctx.config("clippy") .path("ci") .render_steps(), @r" + [clippy] rustc 0 -> bootstrap 1 [build] llvm [build] rustc 0 -> rustc 1 - [check] rustc 1 -> rustc 2 [build] rustc 0 -> clippy-driver 1 [build] rustc 0 -> cargo-clippy 1 - [clippy] bootstrap [clippy] rustc 1 -> std 1 [clippy] rustc 0 -> rustc 1 [clippy] rustc 0 -> rustc_codegen_gcc 1 @@ -2096,21 +2095,20 @@ mod snapshot { [build] llvm [build] rustc 0 -> rustc 1 [build] rustc 1 -> std 1 + [build] rustc 0 -> clippy-driver 1 + [build] rustc 0 -> cargo-clippy 1 + [clippy] rustc 1 -> bootstrap 2 [build] rustc 1 -> rustc 2 - [check] rustc 2 -> rustc 3 [build] rustc 1 -> clippy-driver 2 [build] rustc 1 -> cargo-clippy 2 - [clippy] bootstrap [clippy] rustc 2 -> std 2 - [build] rustc 0 -> clippy-driver 1 - [build] rustc 0 -> cargo-clippy 1 [clippy] rustc 1 -> rustc 2 [clippy] rustc 1 -> rustc_codegen_gcc 2 "); } #[test] - fn clippy_compiler() { + fn clippy_compiler_stage1() { let ctx = TestCtx::new(); insta::assert_snapshot!( ctx.config("clippy") @@ -2122,7 +2120,24 @@ mod snapshot { } #[test] - fn clippy_std() { + fn clippy_compiler_stage2() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx.config("clippy") + .path("compiler") + .stage(2) + .render_steps(), @r" + [build] llvm + [build] rustc 0 -> rustc 1 + [build] rustc 1 -> std 1 + [build] rustc 0 -> clippy-driver 1 + [build] rustc 0 -> cargo-clippy 1 + [clippy] rustc 1 -> rustc 2 + "); + } + + #[test] + fn clippy_std_stage1() { let ctx = TestCtx::new(); insta::assert_snapshot!( ctx.config("clippy") @@ -2135,6 +2150,65 @@ mod snapshot { [clippy] rustc 1 -> std 1 "); } + + #[test] + fn clippy_std_stage2() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx.config("clippy") + .path("std") + .stage(2) + .render_steps(), @r" + [build] llvm + [build] rustc 0 -> rustc 1 + [build] rustc 1 -> std 1 + [build] rustc 1 -> rustc 2 + [build] rustc 1 -> clippy-driver 2 + [build] rustc 1 -> cargo-clippy 2 + [clippy] rustc 2 -> std 2 + "); + } + + #[test] + fn clippy_miri_stage1() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx.config("clippy") + .path("miri") + .stage(1) + .render_steps(), @r" + [build] llvm + [check] rustc 0 -> rustc 1 + [clippy] rustc 0 -> miri 1 + "); + } + + #[test] + fn clippy_miri_stage2() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx.config("clippy") + .path("miri") + .stage(2) + .render_steps(), @r" + [build] llvm + [build] rustc 0 -> rustc 1 + [build] rustc 1 -> std 1 + [check] rustc 1 -> rustc 2 + [build] rustc 0 -> clippy-driver 1 + [build] rustc 0 -> cargo-clippy 1 + [clippy] rustc 1 -> miri 2 + "); + } + + #[test] + fn clippy_bootstrap() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx.config("clippy") + .path("bootstrap") + .render_steps(), @"[clippy] rustc 0 -> bootstrap 1 "); + } } struct ExecutedSteps { From 27347831bd9d90db879fa4bf550f440217dcf81f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Fri, 8 Aug 2025 21:47:18 +0200 Subject: [PATCH 10/15] Lint code in CI using in-tree Clippy --- src/ci/docker/host-x86_64/pr-check-2/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 6fea2437276d0..8073b8efb46f3 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 && \ From 75c5c36c0fe9f583773b2a21910ad920bca6abbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Fri, 8 Aug 2025 21:49:10 +0200 Subject: [PATCH 11/15] Add change tracker entry --- src/bootstrap/src/utils/change_tracker.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs index cd7fba39a8458..b454a8ddefb35 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+.", + }, ]; From 92e1541f76106601bf33d3fe2bc0564b97c85d6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Mon, 11 Aug 2025 19:53:55 +0200 Subject: [PATCH 12/15] Only check rustc when linting rustc_codegen_gcc --- src/bootstrap/src/core/build_steps/clippy.rs | 13 +++++-------- src/bootstrap/src/core/builder/tests.rs | 2 ++ 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/clippy.rs b/src/bootstrap/src/core/build_steps/clippy.rs index b71e5fbb95ca4..2f1adde70162d 100644 --- a/src/bootstrap/src/core/build_steps/clippy.rs +++ b/src/bootstrap/src/core/build_steps/clippy.rs @@ -13,7 +13,7 @@ //! (as usual) a massive undertaking/refactoring. use super::compile::{run_cargo, rustc_cargo, std_cargo}; -use super::tool::{RustcPrivateCompilers, SourceType, prepare_tool_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; @@ -306,7 +306,7 @@ impl Step for Rustc { #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct CodegenGcc { - compilers: RustcPrivateCompilers, + build_compiler: Compiler, target: TargetSelection, config: LintConfig, } @@ -314,7 +314,7 @@ pub struct CodegenGcc { impl CodegenGcc { fn new(builder: &Builder<'_>, target: TargetSelection, config: LintConfig) -> Self { Self { - compilers: RustcPrivateCompilers::new(builder, builder.top_stage, target), + build_compiler: prepare_compiler_for_check(builder, target, Mode::Codegen), target, config, } @@ -335,7 +335,7 @@ impl Step for CodegenGcc { } fn run(self, builder: &Builder<'_>) -> Self::Output { - let build_compiler = self.compilers.build_compiler(); + let build_compiler = self.build_compiler; let target = self.target; let cargo = prepare_tool_cargo( @@ -367,10 +367,7 @@ impl Step for CodegenGcc { } fn metadata(&self) -> Option { - Some( - StepMetadata::clippy("rustc_codegen_gcc", self.target) - .built_by(self.compilers.build_compiler()), - ) + Some(StepMetadata::clippy("rustc_codegen_gcc", self.target).built_by(self.build_compiler)) } } diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 7fdbdd05fb683..226732cd2af98 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -2080,6 +2080,7 @@ mod snapshot { [build] rustc 0 -> cargo-clippy 1 [clippy] rustc 1 -> std 1 [clippy] rustc 0 -> rustc 1 + [check] rustc 0 -> rustc 1 [clippy] rustc 0 -> rustc_codegen_gcc 1 "); } @@ -2103,6 +2104,7 @@ mod snapshot { [build] rustc 1 -> cargo-clippy 2 [clippy] rustc 2 -> std 2 [clippy] rustc 1 -> rustc 2 + [check] rustc 1 -> rustc 2 [clippy] rustc 1 -> rustc_codegen_gcc 2 "); } From 0acfe86faa35eb47ecfe8fb36d4119c8a280e01a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Mon, 11 Aug 2025 20:00:29 +0200 Subject: [PATCH 13/15] Manually optimize steps performed by `x clippy ci` --- src/bootstrap/src/core/build_steps/clippy.rs | 33 ++++++++++++++++++-- src/bootstrap/src/core/builder/tests.rs | 24 +------------- 2 files changed, 32 insertions(+), 25 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/clippy.rs b/src/bootstrap/src/core/build_steps/clippy.rs index 2f1adde70162d..ae35513a5444e 100644 --- a/src/bootstrap/src/core/build_steps/clippy.rs +++ b/src/bootstrap/src/core/build_steps/clippy.rs @@ -12,6 +12,8 @@ //! 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::compile::{run_cargo, rustc_cargo, std_cargo}; use super::tool::{SourceType, prepare_tool_cargo}; use crate::builder::{Builder, ShouldRun}; @@ -154,6 +156,15 @@ impl Std { crates, } } + + fn from_build_compiler( + build_compiler: Compiler, + target: TargetSelection, + config: LintConfig, + crates: Vec, + ) -> Self { + Self { build_compiler, target, config, crates } + } } impl Step for Std { @@ -479,6 +490,7 @@ lint_any!( 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, @@ -499,7 +511,20 @@ 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 { @@ -509,6 +534,7 @@ impl Step for CI { forbid: vec![], }), }); + let library_clippy_cfg = LintConfig { allow: vec!["clippy::all".into()], warn: vec![], @@ -526,8 +552,9 @@ impl Step for CI { ], forbid: vec![], }; - builder.ensure(Std::new( - builder, + 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![], @@ -552,6 +579,7 @@ impl Step for CI { ], forbid: vec![], }; + // This will lint stage 2 rustc using stage 1 Clippy builder.ensure(Rustc::new( builder, self.target, @@ -565,6 +593,7 @@ impl Step for CI { deny: vec!["warnings".into()], forbid: vec![], }; + // This will check stage 2 rustc builder.ensure(CodegenGcc::new( builder, self.target, diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 226732cd2af98..210f05a83464f 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -2068,25 +2068,6 @@ mod snapshot { #[test] fn clippy_ci() { - let ctx = TestCtx::new(); - insta::assert_snapshot!( - ctx.config("clippy") - .path("ci") - .render_steps(), @r" - [clippy] rustc 0 -> bootstrap 1 - [build] llvm - [build] rustc 0 -> rustc 1 - [build] rustc 0 -> clippy-driver 1 - [build] rustc 0 -> cargo-clippy 1 - [clippy] rustc 1 -> std 1 - [clippy] rustc 0 -> rustc 1 - [check] rustc 0 -> rustc 1 - [clippy] rustc 0 -> rustc_codegen_gcc 1 - "); - } - - #[test] - fn clippy_ci_stage_2() { let ctx = TestCtx::new(); insta::assert_snapshot!( ctx.config("clippy") @@ -2099,10 +2080,7 @@ mod snapshot { [build] rustc 0 -> clippy-driver 1 [build] rustc 0 -> cargo-clippy 1 [clippy] rustc 1 -> bootstrap 2 - [build] rustc 1 -> rustc 2 - [build] rustc 1 -> clippy-driver 2 - [build] rustc 1 -> cargo-clippy 2 - [clippy] rustc 2 -> std 2 + [clippy] rustc 1 -> std 1 [clippy] rustc 1 -> rustc 2 [check] rustc 1 -> rustc 2 [clippy] rustc 1 -> rustc_codegen_gcc 2 From 2e4e0fcdb9b412d683ab28632170cc3d22d842c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Mon, 11 Aug 2025 20:33:23 +0200 Subject: [PATCH 14/15] Bless tests --- src/bootstrap/src/core/build_steps/clippy.rs | 3 ++- src/bootstrap/src/core/builder/tests.rs | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/bootstrap/src/core/build_steps/clippy.rs b/src/bootstrap/src/core/build_steps/clippy.rs index ae35513a5444e..23d9a032eb909 100644 --- a/src/bootstrap/src/core/build_steps/clippy.rs +++ b/src/bootstrap/src/core/build_steps/clippy.rs @@ -6,7 +6,8 @@ //! which is performed by the `x clippy ci` command. //! //! In order to prepare a build compiler for running clippy, use the -//! [check::prepare_compiler_for_check] function. That prepares a compiler and a standard library +//! [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 diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 210f05a83464f..8669a17c5e163 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -1516,6 +1516,7 @@ mod snapshot { .path("compiler") .render_steps(), @r" [check] rustc 0 -> rustc 1 + [check] rustc 0 -> rustc 1 [check] rustc 0 -> rustc_codegen_cranelift 1 [check] rustc 0 -> rustc_codegen_gcc 1 "); @@ -1548,6 +1549,7 @@ mod snapshot { .stage(1) .render_steps(), @r" [check] rustc 0 -> rustc 1 + [check] rustc 0 -> rustc 1 [check] rustc 0 -> rustc_codegen_cranelift 1 [check] rustc 0 -> rustc_codegen_gcc 1 "); @@ -1565,6 +1567,7 @@ mod snapshot { [build] rustc 0 -> rustc 1 [build] rustc 1 -> std 1 [check] rustc 1 -> rustc 2 + [check] rustc 1 -> rustc 2 [check] rustc 1 -> rustc_codegen_cranelift 2 [check] rustc 1 -> rustc_codegen_gcc 2 "); @@ -1583,6 +1586,7 @@ mod snapshot { [build] rustc 1 -> std 1 [build] rustc 1 -> std 1 [check] rustc 1 -> rustc 2 + [check] rustc 1 -> rustc 2 [check] rustc 1 -> Rustdoc 2 [check] rustc 1 -> rustc_codegen_cranelift 2 [check] rustc 1 -> rustc_codegen_gcc 2 @@ -1679,6 +1683,7 @@ mod snapshot { .args(&args) .render_steps(), @r" [check] rustc 0 -> rustc 1 + [check] rustc 0 -> rustc 1 [check] rustc 0 -> rustc_codegen_cranelift 1 [check] rustc 0 -> rustc_codegen_gcc 1 "); From 2ea2100c36edc1c12981bedf5821771e5035978a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Wed, 13 Aug 2025 14:36:29 +0200 Subject: [PATCH 15/15] Print crate count for `check::Rustc` in snapshot tests --- src/bootstrap/src/core/build_steps/check.rs | 8 +++++++- src/bootstrap/src/core/builder/tests.rs | 14 ++++++-------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/check.rs b/src/bootstrap/src/core/build_steps/check.rs index ae258bf2939ca..6d393446d458e 100644 --- a/src/bootstrap/src/core/build_steps/check.rs +++ b/src/bootstrap/src/core/build_steps/check.rs @@ -232,7 +232,13 @@ impl Step for Rustc { } fn metadata(&self) -> Option { - 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) } } diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 8669a17c5e163..9ba57542549b5 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -1515,7 +1515,7 @@ mod snapshot { ctx.config("check") .path("compiler") .render_steps(), @r" - [check] rustc 0 -> rustc 1 + [check] rustc 0 -> rustc 1 (73 crates) [check] rustc 0 -> rustc 1 [check] rustc 0 -> rustc_codegen_cranelift 1 [check] rustc 0 -> rustc_codegen_gcc 1 @@ -1528,9 +1528,7 @@ mod snapshot { insta::assert_snapshot!( ctx.config("check") .path("rustc") - .render_steps(), @r" - [check] rustc 0 -> rustc 1 - "); + .render_steps(), @"[check] rustc 0 -> rustc 1 (1 crates)"); } #[test] @@ -1548,7 +1546,7 @@ mod snapshot { .path("compiler") .stage(1) .render_steps(), @r" - [check] rustc 0 -> rustc 1 + [check] rustc 0 -> rustc 1 (73 crates) [check] rustc 0 -> rustc 1 [check] rustc 0 -> rustc_codegen_cranelift 1 [check] rustc 0 -> rustc_codegen_gcc 1 @@ -1566,7 +1564,7 @@ mod snapshot { [build] llvm [build] rustc 0 -> rustc 1 [build] rustc 1 -> std 1 - [check] rustc 1 -> rustc 2 + [check] rustc 1 -> rustc 2 (73 crates) [check] rustc 1 -> rustc 2 [check] rustc 1 -> rustc_codegen_cranelift 2 [check] rustc 1 -> rustc_codegen_gcc 2 @@ -1585,7 +1583,7 @@ mod snapshot { [build] rustc 0 -> rustc 1 [build] rustc 1 -> std 1 [build] rustc 1 -> std 1 - [check] rustc 1 -> rustc 2 + [check] rustc 1 -> rustc 2 (73 crates) [check] rustc 1 -> rustc 2 [check] rustc 1 -> Rustdoc 2 [check] rustc 1 -> rustc_codegen_cranelift 2 @@ -1682,7 +1680,7 @@ mod snapshot { .paths(&["library", "compiler"]) .args(&args) .render_steps(), @r" - [check] rustc 0 -> rustc 1 + [check] rustc 0 -> rustc 1 (73 crates) [check] rustc 0 -> rustc 1 [check] rustc 0 -> rustc_codegen_cranelift 1 [check] rustc 0 -> rustc_codegen_gcc 1