1use super::tool::{SourceType, prepare_tool_cargo};
17use crate::core::build_steps::check::{CompilerForCheck, prepare_compiler_for_check};
18use crate::core::build_steps::compile::{
19 ArtifactKeepMode, run_cargo, rustc_cargo, std_cargo, std_crates_for_make_run,
20};
21use crate::core::builder;
22use crate::core::builder::{
23 Alias, Builder, CommandLineStep, Kind, RunConfig, ShouldRun, StepMetadata, crate_description,
24};
25use crate::core::config::{Subcommand, TargetSelection};
26use crate::utils::build_stamp::{self, BuildStamp};
27use crate::utils::helpers;
28use crate::{Compiler, Mode};
29
30const IGNORED_RULES_FOR_STD_AND_RUSTC: &[&str] = &[
32 "many_single_char_names", "collapsible_if",
34 "type_complexity",
35 "missing_safety_doc", "too_many_arguments",
37 "needless_lifetimes", "wrong_self_convention",
39 "approx_constant", ];
41
42fn lint_args(builder: &Builder<'_>, config: &LintConfig, ignored_rules: &[&str]) -> Vec<String> {
43 fn strings<'a>(arr: &'a [&str]) -> impl Iterator<Item = String> + 'a {
44 arr.iter().copied().map(String::from)
45 }
46
47 let Subcommand::Clippy { fix, allow_dirty, allow_staged, .. } = &builder.config.cmd else {
48 unreachable!("clippy::lint_args can only be called from `clippy` subcommands.");
49 };
50
51 let mut args = vec![];
52 if *fix {
53 #[rustfmt::skip]
54 args.extend(strings(&[
55 "--fix", "-Zunstable-options",
56 "--lib", "--bins", "--examples",
60 ]));
61
62 if *allow_dirty {
63 args.push("--allow-dirty".to_owned());
64 }
65
66 if *allow_staged {
67 args.push("--allow-staged".to_owned());
68 }
69 }
70
71 args.extend(strings(&["--"]));
72
73 if config.deny.is_empty() && config.forbid.is_empty() {
74 args.extend(strings(&["--cap-lints", "warn"]));
75 }
76
77 let all_args = std::env::args().collect::<Vec<_>>();
78 args.extend(get_clippy_rules_in_order(&all_args, config));
79
80 args.extend(ignored_rules.iter().map(|lint| format!("-Aclippy::{lint}")));
81 args.extend(builder.config.free_args.clone());
82 args
83}
84
85pub fn get_clippy_rules_in_order(all_args: &[String], config: &LintConfig) -> Vec<String> {
89 let mut result = vec![];
90
91 for (prefix, item) in
92 [("-A", &config.allow), ("-D", &config.deny), ("-W", &config.warn), ("-F", &config.forbid)]
93 {
94 item.iter().for_each(|v| {
95 let rule = format!("{prefix}{v}");
96 let position = all_args.iter().position(|t| t == &rule || t == v).unwrap_or(usize::MAX);
99 result.push((position, rule));
100 });
101 }
102
103 result.sort_by_key(|&(position, _)| position);
104 result.into_iter().map(|v| v.1).collect()
105}
106
107#[derive(Debug, Clone, PartialEq, Eq, Hash)]
108pub struct LintConfig {
109 pub allow: Vec<String>,
110 pub warn: Vec<String>,
111 pub deny: Vec<String>,
112 pub forbid: Vec<String>,
113}
114
115impl LintConfig {
116 fn new(builder: &Builder<'_>) -> Self {
117 match builder.config.cmd.clone() {
118 Subcommand::Clippy { allow, deny, warn, forbid, .. } => {
119 Self { allow, warn, deny, forbid }
120 }
121 _ => unreachable!("LintConfig can only be called from `clippy` subcommands."),
122 }
123 }
124
125 fn merge(&self, other: &Self) -> Self {
126 let merged = |self_attr: &[String], other_attr: &[String]| -> Vec<String> {
127 self_attr.iter().cloned().chain(other_attr.iter().cloned()).collect()
128 };
129 Self {
131 allow: merged(&self.allow, &other.allow),
132 warn: merged(&self.warn, &other.warn),
133 deny: merged(&self.deny, &other.deny),
134 forbid: merged(&self.forbid, &other.forbid),
135 }
136 }
137}
138
139#[derive(Debug, Clone, PartialEq, Eq, Hash)]
140pub struct Std {
141 build_compiler: Compiler,
142 target: TargetSelection,
143 config: LintConfig,
144 crates: Vec<String>,
146}
147
148impl Std {
149 fn new(
150 builder: &Builder<'_>,
151 target: TargetSelection,
152 config: LintConfig,
153 crates: Vec<String>,
154 ) -> Self {
155 Self {
156 build_compiler: builder.compiler(builder.top_stage, builder.host_target),
157 target,
158 config,
159 crates,
160 }
161 }
162
163 fn from_build_compiler(
164 build_compiler: Compiler,
165 target: TargetSelection,
166 config: LintConfig,
167 crates: Vec<String>,
168 ) -> Self {
169 Self { build_compiler, target, config, crates }
170 }
171}
172
173impl CommandLineStep for Std {
174 type Output = ();
175
176 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
177 run.crate_or_deps("sysroot").path("library")
178 }
179
180 fn is_default_step(_builder: &Builder<'_>) -> bool {
181 true
182 }
183
184 fn make_run(run: RunConfig<'_>) {
185 let crates = std_crates_for_make_run(&run);
186 let config = LintConfig::new(run.builder);
187 run.builder.ensure(Std::new(run.builder, run.target, config, crates));
188 }
189
190 fn run(self, builder: &Builder<'_>) {
191 let target = self.target;
192 let build_compiler = self.build_compiler;
193
194 let mut cargo = builder::Cargo::new(
195 builder,
196 build_compiler,
197 Mode::Std,
198 SourceType::InTree,
199 target,
200 Kind::Clippy,
201 );
202
203 std_cargo(builder, target, &mut cargo, &self.crates);
204
205 let _guard = builder.msg(
206 Kind::Clippy,
207 format_args!("library{}", crate_description(&self.crates)),
208 Mode::Std,
209 build_compiler,
210 target,
211 );
212
213 run_cargo(
214 builder,
215 cargo,
216 lint_args(builder, &self.config, IGNORED_RULES_FOR_STD_AND_RUSTC),
217 &build_stamp::libstd_stamp(builder, build_compiler, target),
218 vec![],
219 ArtifactKeepMode::OnlyRmeta,
220 );
221 }
222
223 fn metadata(&self) -> Option<StepMetadata> {
224 Some(StepMetadata::clippy("std", self.target).built_by(self.build_compiler))
225 }
226}
227
228#[derive(Debug, Clone, PartialEq, Eq, Hash)]
233pub struct Rustc {
234 build_compiler: CompilerForCheck,
235 target: TargetSelection,
236 config: LintConfig,
237 crates: Vec<String>,
239}
240
241impl Rustc {
242 fn new(
243 builder: &Builder<'_>,
244 target: TargetSelection,
245 config: LintConfig,
246 crates: Vec<String>,
247 ) -> Self {
248 Self {
249 build_compiler: prepare_compiler_for_check(builder, target, Mode::Rustc),
250 target,
251 config,
252 crates,
253 }
254 }
255}
256
257impl CommandLineStep for Rustc {
258 type Output = ();
259 const IS_HOST: bool = true;
260
261 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
262 run.crate_or_deps("rustc-main").path("compiler")
263 }
264
265 fn is_default_step(_builder: &Builder<'_>) -> bool {
266 true
267 }
268
269 fn make_run(run: RunConfig<'_>) {
270 let builder = run.builder;
271 let crates = run.make_run_crates(Alias::Compiler);
272 let config = LintConfig::new(run.builder);
273 run.builder.ensure(Rustc::new(builder, run.target, config, crates));
274 }
275
276 fn run(self, builder: &Builder<'_>) {
277 let build_compiler = self.build_compiler.build_compiler();
278 let target = self.target;
279
280 let mut cargo = builder::Cargo::new(
281 builder,
282 build_compiler,
283 Mode::Rustc,
284 SourceType::InTree,
285 target,
286 Kind::Clippy,
287 );
288
289 rustc_cargo(builder, &mut cargo, target, &build_compiler, &self.crates);
290 self.build_compiler.configure_cargo(&mut cargo);
291
292 for krate in &*self.crates {
296 cargo.arg("-p").arg(krate);
297 }
298
299 let _guard = builder.msg(
300 Kind::Clippy,
301 format_args!("compiler{}", crate_description(&self.crates)),
302 Mode::Rustc,
303 build_compiler,
304 target,
305 );
306
307 run_cargo(
308 builder,
309 cargo,
310 lint_args(builder, &self.config, IGNORED_RULES_FOR_STD_AND_RUSTC),
311 &build_stamp::librustc_stamp(builder, build_compiler, target),
312 vec![],
313 ArtifactKeepMode::OnlyRmeta,
314 );
315 }
316
317 fn metadata(&self) -> Option<StepMetadata> {
318 Some(
319 StepMetadata::clippy("rustc", self.target)
320 .built_by(self.build_compiler.build_compiler()),
321 )
322 }
323}
324
325#[derive(Debug, Clone, Hash, PartialEq, Eq)]
326pub struct CodegenGcc {
327 build_compiler: CompilerForCheck,
328 target: TargetSelection,
329 config: LintConfig,
330}
331
332impl CodegenGcc {
333 fn new(builder: &Builder<'_>, target: TargetSelection, config: LintConfig) -> Self {
334 Self {
335 build_compiler: prepare_compiler_for_check(builder, target, Mode::Codegen),
336 target,
337 config,
338 }
339 }
340}
341
342impl CommandLineStep for CodegenGcc {
343 type Output = ();
344
345 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
346 run.alias("rustc_codegen_gcc")
347 }
348
349 fn make_run(run: RunConfig<'_>) {
350 let builder = run.builder;
351 let config = LintConfig::new(builder);
352 builder.ensure(CodegenGcc::new(builder, run.target, config));
353 }
354
355 fn run(self, builder: &Builder<'_>) -> Self::Output {
356 let build_compiler = self.build_compiler.build_compiler();
357 let target = self.target;
358
359 let mut cargo = prepare_tool_cargo(
360 builder,
361 build_compiler,
362 Mode::Codegen,
363 target,
364 Kind::Clippy,
365 "compiler/rustc_codegen_gcc",
366 SourceType::InTree,
367 &[],
368 );
369 self.build_compiler.configure_cargo(&mut cargo);
370
371 let _guard = builder.msg(
372 Kind::Clippy,
373 "rustc_codegen_gcc",
374 Mode::ToolRustcPrivate,
375 build_compiler,
376 target,
377 );
378
379 let stamp = BuildStamp::new(&builder.cargo_out(build_compiler, Mode::Codegen, target))
380 .with_prefix("rustc_codegen_gcc-check");
381
382 let args = lint_args(builder, &self.config, &[]);
383 run_cargo(builder, cargo, args.clone(), &stamp, vec![], ArtifactKeepMode::OnlyRmeta);
384
385 let mut cargo = prepare_tool_cargo(
387 builder,
388 build_compiler,
389 Mode::Codegen,
390 target,
391 Kind::Clippy,
392 "compiler/rustc_codegen_gcc",
393 SourceType::InTree,
394 &[],
395 );
396 self.build_compiler.configure_cargo(&mut cargo);
397 println!("Now running clippy on `rustc_codegen_gcc` with `--no-default-features`");
398 cargo.arg("--no-default-features");
399 run_cargo(builder, cargo, args, &stamp, vec![], ArtifactKeepMode::OnlyRmeta);
400 }
401
402 fn metadata(&self) -> Option<StepMetadata> {
403 Some(
404 StepMetadata::clippy("rustc_codegen_gcc", self.target)
405 .built_by(self.build_compiler.build_compiler()),
406 )
407 }
408}
409
410macro_rules! lint_any {
411 ($(
412 $name:ident,
413 $path:expr,
414 $readable_name:expr,
415 $mode:expr
416 $(, lint_by_default = $lint_by_default:expr )?
417 ;
418 )+) => {
419 $(
420
421 #[derive(Debug, Clone, Hash, PartialEq, Eq)]
422 pub struct $name {
423 build_compiler: CompilerForCheck,
424 target: TargetSelection,
425 config: LintConfig,
426 }
427
428 impl CommandLineStep for $name {
429 type Output = ();
430
431 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
432 run.path($path)
433 }
434
435 fn is_default_step(_builder: &Builder<'_>) -> bool {
436 false $( || const { $lint_by_default } )?
437 }
438
439 fn make_run(run: RunConfig<'_>) {
440 let config = LintConfig::new(run.builder);
441 run.builder.ensure($name {
442 build_compiler: prepare_compiler_for_check(run.builder, run.target, $mode),
443 target: run.target,
444 config,
445 });
446 }
447
448 fn run(self, builder: &Builder<'_>) -> Self::Output {
449 let build_compiler = self.build_compiler.build_compiler();
450 let target = self.target;
451 let mut cargo = prepare_tool_cargo(
452 builder,
453 build_compiler,
454 $mode,
455 target,
456 Kind::Clippy,
457 $path,
458 SourceType::InTree,
459 &[],
460 );
461 self.build_compiler.configure_cargo(&mut cargo);
462
463 let _guard = builder.msg(
464 Kind::Clippy,
465 $readable_name,
466 $mode,
467 build_compiler,
468 target,
469 );
470
471 let stringified_name = stringify!($name).to_lowercase();
472 let stamp = BuildStamp::new(&builder.cargo_out(build_compiler, $mode, target))
473 .with_prefix(&format!("{}-check", stringified_name));
474
475 run_cargo(
476 builder,
477 cargo,
478 lint_args(builder, &self.config, &[]),
479 &stamp,
480 vec![],
481 ArtifactKeepMode::OnlyRmeta
482 );
483 }
484
485 fn metadata(&self) -> Option<StepMetadata> {
486 Some(StepMetadata::clippy($readable_name, self.target).built_by(self.build_compiler.build_compiler()))
487 }
488 }
489 )+
490 }
491}
492
493lint_any!(
496 Bootstrap, "src/bootstrap", "bootstrap", Mode::ToolTarget;
497 BuildHelper, "src/build_helper", "build_helper", Mode::ToolTarget;
498 BuildManifest, "src/tools/build-manifest", "build-manifest", Mode::ToolTarget;
499 CargoMiri, "src/tools/miri/cargo-miri", "cargo-miri", Mode::ToolRustcPrivate;
500 Clippy, "src/tools/clippy", "clippy", Mode::ToolRustcPrivate;
501 CollectLicenseMetadata, "src/tools/collect-license-metadata", "collect-license-metadata", Mode::ToolTarget;
502 Compiletest, "src/tools/compiletest", "compiletest", Mode::ToolTarget;
503 CoverageDump, "src/tools/coverage-dump", "coverage-dump", Mode::ToolTarget;
504 Jsondocck, "src/tools/jsondocck", "jsondocck", Mode::ToolTarget;
505 Jsondoclint, "src/tools/jsondoclint", "jsondoclint", Mode::ToolTarget;
506 LintDocs, "src/tools/lint-docs", "lint-docs", Mode::ToolTarget;
507 LlvmBitcodeLinker, "src/tools/llvm-bitcode-linker", "llvm-bitcode-linker", Mode::ToolTarget;
508 Miri, "src/tools/miri", "miri", Mode::ToolRustcPrivate;
509 MiroptTestTools, "src/tools/miropt-test-tools", "miropt-test-tools", Mode::ToolTarget;
510 OptDist, "src/tools/opt-dist", "opt-dist", Mode::ToolTarget;
511 RemoteTestClient, "src/tools/remote-test-client", "remote-test-client", Mode::ToolTarget;
512 RemoteTestServer, "src/tools/remote-test-server", "remote-test-server", Mode::ToolTarget;
513 RustAnalyzer, "src/tools/rust-analyzer", "rust-analyzer", Mode::ToolRustcPrivate;
514 Rustdoc, "src/librustdoc", "clippy", Mode::ToolRustcPrivate;
515 Rustfmt, "src/tools/rustfmt", "rustfmt", Mode::ToolRustcPrivate;
516 RustInstaller, "src/tools/rust-installer", "rust-installer", Mode::ToolTarget;
517 Tidy, "src/tools/tidy", "tidy", Mode::ToolTarget;
518 TestFloatParse, "src/tools/test-float-parse", "test-float-parse", Mode::ToolStd;
519);
520
521#[derive(Debug, Clone, PartialEq, Eq, Hash)]
523pub struct CI {
524 target: TargetSelection,
525 config: LintConfig,
526}
527
528impl CommandLineStep for CI {
529 type Output = ();
530
531 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
532 run.alias("ci")
533 }
534
535 fn is_default_step(_builder: &Builder<'_>) -> bool {
536 false
537 }
538
539 fn make_run(run: RunConfig<'_>) {
540 let config = LintConfig::new(run.builder);
541 run.builder.ensure(CI { target: run.target, config });
542 }
543
544 fn run(self, builder: &Builder<'_>) -> Self::Output {
545 if builder.top_stage != 2 {
546 eprintln!("ERROR: `x clippy ci` should always be executed with --stage 2");
547 helpers::exit_process(1);
548 }
549
550 builder.ensure(Bootstrap {
558 build_compiler: prepare_compiler_for_check(builder, self.target, Mode::ToolTarget),
560 target: self.target,
561 config: self.config.merge(&LintConfig {
562 allow: vec![],
563 warn: vec![],
564 deny: vec!["warnings".into()],
565 forbid: vec![],
566 }),
567 });
568
569 let library_clippy_cfg = LintConfig {
570 allow: vec!["clippy::all".into()],
571 warn: vec![],
572 deny: vec![
573 "clippy::correctness".into(),
574 "clippy::char_lit_as_u8".into(),
575 "clippy::four_forward_slashes".into(),
576 "clippy::needless_bool".into(),
577 "clippy::needless_bool_assign".into(),
578 "clippy::non_minimal_cfg".into(),
579 "clippy::print_literal".into(),
580 "clippy::same_item_push".into(),
581 "clippy::single_char_add_str".into(),
582 "clippy::to_string_in_format_args".into(),
583 "clippy::unconditional_recursion".into(),
584 "clippy::int_plus_one".into(),
585 "clippy::legacy_numeric_constants".into(),
586 "clippy::zero_divided_by_zero".into(),
587 "clippy::len_zero".into(),
588 "clippy::needless_as_bytes".into(),
589 "clippy::ptr_offset_with_cast".into(),
590 "clippy::let_and_return".into(),
591 "clippy::needless_return".into(),
592 ],
593 forbid: vec![],
594 };
595 builder.ensure(Std::from_build_compiler(
596 builder.compiler(1, self.target),
598 self.target,
599 self.config.merge(&library_clippy_cfg),
600 vec![],
601 ));
602
603 let compiler_clippy_cfg = LintConfig {
604 allow: vec!["clippy::all".into()],
605 warn: vec![],
606 deny: vec![
607 "clippy::correctness".into(),
608 "clippy::char_lit_as_u8".into(),
609 "clippy::clone_on_ref_ptr".into(),
610 "clippy::format_in_format_args".into(),
611 "clippy::four_forward_slashes".into(),
612 "clippy::needless_bool".into(),
613 "clippy::needless_bool_assign".into(),
614 "clippy::non_minimal_cfg".into(),
615 "clippy::print_literal".into(),
616 "clippy::same_item_push".into(),
617 "clippy::single_char_add_str".into(),
618 "clippy::to_string_in_format_args".into(),
619 "clippy::unconditional_recursion".into(),
620 "clippy::mem_replace_with_default".into(),
621 ],
622 forbid: vec![],
623 };
624 builder.ensure(Rustc::new(
626 builder,
627 self.target,
628 self.config.merge(&compiler_clippy_cfg),
629 vec![],
630 ));
631
632 let rustc_codegen_gcc = LintConfig {
633 allow: vec![],
634 warn: vec![],
635 deny: vec!["warnings".into()],
636 forbid: vec![],
637 };
638 builder.ensure(CodegenGcc::new(
640 builder,
641 self.target,
642 self.config.merge(&rustc_codegen_gcc),
643 ));
644 }
645}