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