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 const DEFAULT: bool = true;
174
175 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
176 run.crate_or_deps("sysroot").path("library")
177 }
178
179 fn make_run(run: RunConfig<'_>) {
180 let crates = std_crates_for_run_make(&run);
181 let config = LintConfig::new(run.builder);
182 run.builder.ensure(Std::new(run.builder, run.target, config, crates));
183 }
184
185 fn run(self, builder: &Builder<'_>) {
186 let target = self.target;
187 let build_compiler = self.build_compiler;
188
189 let mut cargo = builder::Cargo::new(
190 builder,
191 build_compiler,
192 Mode::Std,
193 SourceType::InTree,
194 target,
195 Kind::Clippy,
196 );
197
198 std_cargo(builder, target, &mut cargo, &self.crates);
199
200 let _guard = builder.msg(
201 Kind::Clippy,
202 format_args!("library{}", crate_description(&self.crates)),
203 Mode::Std,
204 build_compiler,
205 target,
206 );
207
208 run_cargo(
209 builder,
210 cargo,
211 lint_args(builder, &self.config, IGNORED_RULES_FOR_STD_AND_RUSTC),
212 &build_stamp::libstd_stamp(builder, build_compiler, target),
213 vec![],
214 true,
215 false,
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 const DEFAULT: bool = true;
257
258 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
259 run.crate_or_deps("rustc-main").path("compiler")
260 }
261
262 fn make_run(run: RunConfig<'_>) {
263 let builder = run.builder;
264 let crates = run.make_run_crates(Alias::Compiler);
265 let config = LintConfig::new(run.builder);
266 run.builder.ensure(Rustc::new(builder, run.target, config, crates));
267 }
268
269 fn run(self, builder: &Builder<'_>) {
270 let build_compiler = self.build_compiler.build_compiler();
271 let target = self.target;
272
273 let mut cargo = builder::Cargo::new(
274 builder,
275 build_compiler,
276 Mode::Rustc,
277 SourceType::InTree,
278 target,
279 Kind::Clippy,
280 );
281
282 rustc_cargo(builder, &mut cargo, target, &build_compiler, &self.crates);
283 self.build_compiler.configure_cargo(&mut cargo);
284
285 for krate in &*self.crates {
289 cargo.arg("-p").arg(krate);
290 }
291
292 let _guard = builder.msg(
293 Kind::Clippy,
294 format_args!("compiler{}", crate_description(&self.crates)),
295 Mode::Rustc,
296 build_compiler,
297 target,
298 );
299
300 run_cargo(
301 builder,
302 cargo,
303 lint_args(builder, &self.config, IGNORED_RULES_FOR_STD_AND_RUSTC),
304 &build_stamp::librustc_stamp(builder, build_compiler, target),
305 vec![],
306 true,
307 false,
308 );
309 }
310
311 fn metadata(&self) -> Option<StepMetadata> {
312 Some(
313 StepMetadata::clippy("rustc", self.target)
314 .built_by(self.build_compiler.build_compiler()),
315 )
316 }
317}
318
319#[derive(Debug, Clone, Hash, PartialEq, Eq)]
320pub struct CodegenGcc {
321 build_compiler: CompilerForCheck,
322 target: TargetSelection,
323 config: LintConfig,
324}
325
326impl CodegenGcc {
327 fn new(builder: &Builder<'_>, target: TargetSelection, config: LintConfig) -> Self {
328 Self {
329 build_compiler: prepare_compiler_for_check(builder, target, Mode::Codegen),
330 target,
331 config,
332 }
333 }
334}
335
336impl Step for CodegenGcc {
337 type Output = ();
338
339 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
340 run.alias("rustc_codegen_gcc")
341 }
342
343 fn make_run(run: RunConfig<'_>) {
344 let builder = run.builder;
345 let config = LintConfig::new(builder);
346 builder.ensure(CodegenGcc::new(builder, run.target, config));
347 }
348
349 fn run(self, builder: &Builder<'_>) -> Self::Output {
350 let build_compiler = self.build_compiler.build_compiler();
351 let target = self.target;
352
353 let mut cargo = prepare_tool_cargo(
354 builder,
355 build_compiler,
356 Mode::Codegen,
357 target,
358 Kind::Clippy,
359 "compiler/rustc_codegen_gcc",
360 SourceType::InTree,
361 &[],
362 );
363 self.build_compiler.configure_cargo(&mut cargo);
364
365 let _guard = builder.msg(
366 Kind::Clippy,
367 "rustc_codegen_gcc",
368 Mode::ToolRustcPrivate,
369 build_compiler,
370 target,
371 );
372
373 let stamp = BuildStamp::new(&builder.cargo_out(build_compiler, Mode::Codegen, target))
374 .with_prefix("rustc_codegen_gcc-check");
375
376 run_cargo(
377 builder,
378 cargo,
379 lint_args(builder, &self.config, &[]),
380 &stamp,
381 vec![],
382 true,
383 false,
384 );
385 }
386
387 fn metadata(&self) -> Option<StepMetadata> {
388 Some(
389 StepMetadata::clippy("rustc_codegen_gcc", self.target)
390 .built_by(self.build_compiler.build_compiler()),
391 )
392 }
393}
394
395macro_rules! lint_any {
396 ($(
397 $name:ident,
398 $path:expr,
399 $readable_name:expr,
400 $mode:expr
401 $(,lint_by_default = $lint_by_default:expr)*
402 ;
403 )+) => {
404 $(
405
406 #[derive(Debug, Clone, Hash, PartialEq, Eq)]
407 pub struct $name {
408 build_compiler: CompilerForCheck,
409 target: TargetSelection,
410 config: LintConfig,
411 }
412
413 impl Step for $name {
414 type Output = ();
415 const DEFAULT: bool = if false $(|| $lint_by_default)* { true } else { false };
416
417 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
418 run.path($path)
419 }
420
421 fn make_run(run: RunConfig<'_>) {
422 let config = LintConfig::new(run.builder);
423 run.builder.ensure($name {
424 build_compiler: prepare_compiler_for_check(run.builder, run.target, $mode),
425 target: run.target,
426 config,
427 });
428 }
429
430 fn run(self, builder: &Builder<'_>) -> Self::Output {
431 let build_compiler = self.build_compiler.build_compiler();
432 let target = self.target;
433 let mut cargo = prepare_tool_cargo(
434 builder,
435 build_compiler,
436 $mode,
437 target,
438 Kind::Clippy,
439 $path,
440 SourceType::InTree,
441 &[],
442 );
443 self.build_compiler.configure_cargo(&mut cargo);
444
445 let _guard = builder.msg(
446 Kind::Clippy,
447 $readable_name,
448 $mode,
449 build_compiler,
450 target,
451 );
452
453 let stringified_name = stringify!($name).to_lowercase();
454 let stamp = BuildStamp::new(&builder.cargo_out(build_compiler, $mode, target))
455 .with_prefix(&format!("{}-check", stringified_name));
456
457 run_cargo(
458 builder,
459 cargo,
460 lint_args(builder, &self.config, &[]),
461 &stamp,
462 vec![],
463 true,
464 false,
465 );
466 }
467
468 fn metadata(&self) -> Option<StepMetadata> {
469 Some(StepMetadata::clippy($readable_name, self.target).built_by(self.build_compiler.build_compiler()))
470 }
471 }
472 )+
473 }
474}
475
476lint_any!(
479 Bootstrap, "src/bootstrap", "bootstrap", Mode::ToolTarget;
480 BuildHelper, "src/build_helper", "build_helper", Mode::ToolTarget;
481 BuildManifest, "src/tools/build-manifest", "build-manifest", Mode::ToolTarget;
482 CargoMiri, "src/tools/miri/cargo-miri", "cargo-miri", Mode::ToolRustcPrivate;
483 Clippy, "src/tools/clippy", "clippy", Mode::ToolRustcPrivate;
484 CollectLicenseMetadata, "src/tools/collect-license-metadata", "collect-license-metadata", Mode::ToolTarget;
485 Compiletest, "src/tools/compiletest", "compiletest", Mode::ToolTarget;
486 CoverageDump, "src/tools/coverage-dump", "coverage-dump", Mode::ToolTarget;
487 Jsondocck, "src/tools/jsondocck", "jsondocck", Mode::ToolTarget;
488 Jsondoclint, "src/tools/jsondoclint", "jsondoclint", Mode::ToolTarget;
489 LintDocs, "src/tools/lint-docs", "lint-docs", Mode::ToolTarget;
490 LlvmBitcodeLinker, "src/tools/llvm-bitcode-linker", "llvm-bitcode-linker", Mode::ToolTarget;
491 Miri, "src/tools/miri", "miri", Mode::ToolRustcPrivate;
492 MiroptTestTools, "src/tools/miropt-test-tools", "miropt-test-tools", Mode::ToolTarget;
493 OptDist, "src/tools/opt-dist", "opt-dist", Mode::ToolTarget;
494 RemoteTestClient, "src/tools/remote-test-client", "remote-test-client", Mode::ToolTarget;
495 RemoteTestServer, "src/tools/remote-test-server", "remote-test-server", Mode::ToolTarget;
496 RustAnalyzer, "src/tools/rust-analyzer", "rust-analyzer", Mode::ToolRustcPrivate;
497 Rustdoc, "src/librustdoc", "clippy", Mode::ToolRustcPrivate;
498 Rustfmt, "src/tools/rustfmt", "rustfmt", Mode::ToolRustcPrivate;
499 RustInstaller, "src/tools/rust-installer", "rust-installer", Mode::ToolTarget;
500 Tidy, "src/tools/tidy", "tidy", Mode::ToolTarget;
501 TestFloatParse, "src/tools/test-float-parse", "test-float-parse", Mode::ToolStd;
502);
503
504#[derive(Debug, Clone, PartialEq, Eq, Hash)]
506pub struct CI {
507 target: TargetSelection,
508 config: LintConfig,
509}
510
511impl Step for CI {
512 type Output = ();
513 const DEFAULT: bool = false;
514
515 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
516 run.alias("ci")
517 }
518
519 fn make_run(run: RunConfig<'_>) {
520 let config = LintConfig::new(run.builder);
521 run.builder.ensure(CI { target: run.target, config });
522 }
523
524 fn run(self, builder: &Builder<'_>) -> Self::Output {
525 if builder.top_stage != 2 {
526 eprintln!("ERROR: `x clippy ci` should always be executed with --stage 2");
527 exit!(1);
528 }
529
530 builder.ensure(Bootstrap {
538 build_compiler: prepare_compiler_for_check(builder, self.target, Mode::ToolTarget),
540 target: self.target,
541 config: self.config.merge(&LintConfig {
542 allow: vec![],
543 warn: vec![],
544 deny: vec!["warnings".into()],
545 forbid: vec![],
546 }),
547 });
548
549 let library_clippy_cfg = LintConfig {
550 allow: vec!["clippy::all".into()],
551 warn: vec![],
552 deny: vec![
553 "clippy::correctness".into(),
554 "clippy::char_lit_as_u8".into(),
555 "clippy::four_forward_slashes".into(),
556 "clippy::needless_bool".into(),
557 "clippy::needless_bool_assign".into(),
558 "clippy::non_minimal_cfg".into(),
559 "clippy::print_literal".into(),
560 "clippy::same_item_push".into(),
561 "clippy::single_char_add_str".into(),
562 "clippy::to_string_in_format_args".into(),
563 "clippy::unconditional_recursion".into(),
564 ],
565 forbid: vec![],
566 };
567 builder.ensure(Std::from_build_compiler(
568 builder.compiler(1, self.target),
570 self.target,
571 self.config.merge(&library_clippy_cfg),
572 vec![],
573 ));
574
575 let compiler_clippy_cfg = LintConfig {
576 allow: vec!["clippy::all".into()],
577 warn: vec![],
578 deny: vec![
579 "clippy::correctness".into(),
580 "clippy::char_lit_as_u8".into(),
581 "clippy::clone_on_ref_ptr".into(),
582 "clippy::format_in_format_args".into(),
583 "clippy::four_forward_slashes".into(),
584 "clippy::needless_bool".into(),
585 "clippy::needless_bool_assign".into(),
586 "clippy::non_minimal_cfg".into(),
587 "clippy::print_literal".into(),
588 "clippy::same_item_push".into(),
589 "clippy::single_char_add_str".into(),
590 "clippy::to_string_in_format_args".into(),
591 "clippy::unconditional_recursion".into(),
592 ],
593 forbid: vec![],
594 };
595 builder.ensure(Rustc::new(
597 builder,
598 self.target,
599 self.config.merge(&compiler_clippy_cfg),
600 vec![],
601 ));
602
603 let rustc_codegen_gcc = LintConfig {
604 allow: vec![],
605 warn: vec![],
606 deny: vec!["warnings".into()],
607 forbid: vec![],
608 };
609 builder.ensure(CodegenGcc::new(
611 builder,
612 self.target,
613 self.config.merge(&rustc_codegen_gcc),
614 ));
615 }
616}