diff --git a/src/bootstrap/src/core/build_steps/clippy.rs b/src/bootstrap/src/core/build_steps/clippy.rs index 01b5e99116ff2..38445d13ac06d 100644 --- a/src/bootstrap/src/core/build_steps/clippy.rs +++ b/src/bootstrap/src/core/build_steps/clippy.rs @@ -35,14 +35,12 @@ const IGNORED_RULES_FOR_STD_AND_RUSTC: &[&str] = &[ "wrong_self_convention", ]; -fn lint_args(builder: &Builder<'_>, ignored_rules: &[&str]) -> Vec { +fn lint_args(builder: &Builder<'_>, config: &LintConfig, ignored_rules: &[&str]) -> Vec { fn strings<'a>(arr: &'a [&str]) -> impl Iterator + 'a { arr.iter().copied().map(String::from) } - let Subcommand::Clippy { fix, allow_dirty, allow_staged, allow, deny, warn, forbid } = - &builder.config.cmd - else { + let Subcommand::Clippy { fix, allow_dirty, allow_staged, .. } = &builder.config.cmd else { unreachable!("clippy::lint_args can only be called from `clippy` subcommands."); }; @@ -68,12 +66,12 @@ fn lint_args(builder: &Builder<'_>, ignored_rules: &[&str]) -> Vec { args.extend(strings(&["--"])); - if deny.is_empty() && forbid.is_empty() { + if config.deny.is_empty() && config.forbid.is_empty() { args.extend(strings(&["--cap-lints", "warn"])); } let all_args = std::env::args().collect::>(); - args.extend(get_clippy_rules_in_order(&all_args, allow, deny, warn, forbid)); + args.extend(get_clippy_rules_in_order(&all_args, config)); args.extend(ignored_rules.iter().map(|lint| format!("-Aclippy::{}", lint))); args.extend(builder.config.free_args.clone()); @@ -83,21 +81,17 @@ fn lint_args(builder: &Builder<'_>, ignored_rules: &[&str]) -> Vec { /// We need to keep the order of the given clippy lint rules before passing them. /// Since clap doesn't offer any useful interface for this purpose out of the box, /// we have to handle it manually. -pub(crate) fn get_clippy_rules_in_order( - all_args: &[String], - allow_rules: &[String], - deny_rules: &[String], - warn_rules: &[String], - forbid_rules: &[String], -) -> Vec { +fn get_clippy_rules_in_order(all_args: &[String], config: &LintConfig) -> Vec { let mut result = vec![]; for (prefix, item) in - [("-A", allow_rules), ("-D", deny_rules), ("-W", warn_rules), ("-F", forbid_rules)] + [("-A", &config.allow), ("-D", &config.deny), ("-W", &config.warn), ("-F", &config.forbid)] { item.iter().for_each(|v| { let rule = format!("{prefix}{v}"); - let position = all_args.iter().position(|t| t == &rule).unwrap(); + // Arguments added by bootstrap in LintConfig won't show up in the all_args list, so + // put them at the end of the command line. + let position = all_args.iter().position(|t| t == &rule).unwrap_or(usize::MAX); result.push((position, rule)); }); } @@ -106,9 +100,42 @@ pub(crate) fn get_clippy_rules_in_order( result.into_iter().map(|v| v.1).collect() } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +struct LintConfig { + allow: Vec, + warn: Vec, + deny: Vec, + forbid: Vec, +} + +impl LintConfig { + fn new(builder: &Builder<'_>) -> Self { + match builder.config.cmd.clone() { + Subcommand::Clippy { allow, deny, warn, forbid, .. } => { + Self { allow, warn, deny, forbid } + } + _ => unreachable!("LintConfig can only be called from `clippy` subcommands."), + } + } + + fn merge(&self, other: &Self) -> Self { + let merged = |self_attr: &[String], other_attr: &[String]| -> Vec { + self_attr.iter().cloned().chain(other_attr.iter().cloned()).collect() + }; + // This is written this way to ensure we get a compiler error if we add a new field. + Self { + allow: merged(&self.allow, &other.allow), + warn: merged(&self.warn, &other.warn), + deny: merged(&self.deny, &other.deny), + forbid: merged(&self.forbid, &other.forbid), + } + } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Std { pub target: TargetSelection, + config: LintConfig, /// Whether to lint only a subset of crates. crates: Vec, } @@ -123,7 +150,8 @@ impl Step for Std { fn make_run(run: RunConfig<'_>) { let crates = run.make_run_crates(Alias::Library); - run.builder.ensure(Std { target: run.target, crates }); + let config = LintConfig::new(run.builder); + run.builder.ensure(Std { target: run.target, config, crates }); } fn run(self, builder: &Builder<'_>) { @@ -147,7 +175,7 @@ impl Step for Std { run_cargo( builder, cargo, - lint_args(builder, IGNORED_RULES_FOR_STD_AND_RUSTC), + lint_args(builder, &self.config, IGNORED_RULES_FOR_STD_AND_RUSTC), &libstd_stamp(builder, compiler, target), vec![], true, @@ -159,6 +187,7 @@ impl Step for Std { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Rustc { pub target: TargetSelection, + config: LintConfig, /// Whether to lint only a subset of crates. crates: Vec, } @@ -174,7 +203,8 @@ impl Step for Rustc { fn make_run(run: RunConfig<'_>) { let crates = run.make_run_crates(Alias::Compiler); - run.builder.ensure(Rustc { target: run.target, crates }); + let config = LintConfig::new(run.builder); + run.builder.ensure(Rustc { target: run.target, config, crates }); } /// Lints the compiler. @@ -221,7 +251,7 @@ impl Step for Rustc { run_cargo( builder, cargo, - lint_args(builder, IGNORED_RULES_FOR_STD_AND_RUSTC), + lint_args(builder, &self.config, IGNORED_RULES_FOR_STD_AND_RUSTC), &librustc_stamp(builder, compiler, target), vec![], true, @@ -241,6 +271,7 @@ macro_rules! lint_any { #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct $name { pub target: TargetSelection, + config: LintConfig, } impl Step for $name { @@ -252,8 +283,10 @@ macro_rules! lint_any { } fn make_run(run: RunConfig<'_>) { + let config = LintConfig::new(run.builder); run.builder.ensure($name { target: run.target, + config, }); } @@ -290,7 +323,7 @@ macro_rules! lint_any { run_cargo( builder, cargo, - lint_args(builder, &[]), + lint_args(builder, &self.config, &[]), &stamp, vec![], true, @@ -328,3 +361,52 @@ lint_any!( RustInstaller, "src/tools/rust-installer", "rust-installer"; Tidy, "src/tools/tidy", "tidy"; ); + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct CI { + target: TargetSelection, + config: LintConfig, +} + +impl Step for CI { + type Output = (); + const DEFAULT: bool = false; + + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.alias("ci") + } + + fn make_run(run: RunConfig<'_>) { + let config = LintConfig::new(run.builder); + run.builder.ensure(CI { target: run.target, config }); + } + + fn run(self, builder: &Builder<'_>) -> Self::Output { + builder.ensure(Bootstrap { + target: self.target, + config: self.config.merge(&LintConfig { + allow: vec![], + warn: vec![], + deny: vec!["warnings".into()], + forbid: vec![], + }), + }); + + let correctness = LintConfig { + allow: vec!["clippy::all".into()], + warn: vec![], + deny: vec!["clippy::correctness".into()], + forbid: vec![], + }; + builder.ensure(Std { + target: self.target, + config: self.config.merge(&correctness), + crates: vec![], + }); + builder.ensure(Rustc { + target: self.target, + config: self.config.merge(&correctness), + crates: vec![], + }); + } +} diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder.rs index 12d2bb18ab7ca..24a2e2bbd963d 100644 --- a/src/bootstrap/src/core/builder.rs +++ b/src/bootstrap/src/core/builder.rs @@ -779,6 +779,7 @@ impl<'a> Builder<'a> { clippy::Rustfmt, clippy::RustInstaller, clippy::Tidy, + clippy::CI, ), Kind::Check | Kind::Fix => describe!( check::Std, diff --git a/src/ci/docker/host-x86_64/mingw-check/Dockerfile b/src/ci/docker/host-x86_64/mingw-check/Dockerfile index 0d9c21c4487bf..09e85e74f3d0a 100644 --- a/src/ci/docker/host-x86_64/mingw-check/Dockerfile +++ b/src/ci/docker/host-x86_64/mingw-check/Dockerfile @@ -47,8 +47,7 @@ ENV RUN_CHECK_WITH_PARALLEL_QUERIES 1 # We also skip the x86_64-unknown-linux-gnu target as it is well-tested by other jobs. ENV SCRIPT python3 ../x.py check --stage 0 --set build.optimized-compiler-builtins=false core alloc std --target=aarch64-unknown-linux-gnu,i686-pc-windows-msvc,i686-unknown-linux-gnu,x86_64-apple-darwin,x86_64-pc-windows-gnu,x86_64-pc-windows-msvc && \ python3 ../x.py check --target=i686-pc-windows-gnu --host=i686-pc-windows-gnu && \ - python3 ../x.py clippy bootstrap -Dwarnings && \ - python3 ../x.py clippy compiler library -Aclippy::all -Dclippy::correctness && \ + python3 ../x.py clippy ci && \ python3 ../x.py build --stage 0 src/tools/build-manifest && \ python3 ../x.py test --stage 0 src/tools/compiletest && \ python3 ../x.py test --stage 0 core alloc std test proc_macro && \