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