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