1use super::check;
4use super::compile::{run_cargo, rustc_cargo, std_cargo};
5use super::tool::{SourceType, prepare_tool_cargo};
6use crate::builder::{Builder, ShouldRun};
7use crate::core::build_steps::compile::std_crates_for_run_make;
8use crate::core::builder;
9use crate::core::builder::{Alias, Kind, RunConfig, Step, crate_description};
10use crate::utils::build_stamp::{self, BuildStamp};
11use crate::{Mode, Subcommand, TargetSelection};
12
13const IGNORED_RULES_FOR_STD_AND_RUSTC: &[&str] = &[
15 "many_single_char_names", "collapsible_if",
17 "type_complexity",
18 "missing_safety_doc", "too_many_arguments",
20 "needless_lifetimes", "wrong_self_convention",
22 "approx_constant", ];
24
25fn lint_args(builder: &Builder<'_>, config: &LintConfig, ignored_rules: &[&str]) -> Vec<String> {
26 fn strings<'a>(arr: &'a [&str]) -> impl Iterator<Item = String> + 'a {
27 arr.iter().copied().map(String::from)
28 }
29
30 let Subcommand::Clippy { fix, allow_dirty, allow_staged, .. } = &builder.config.cmd else {
31 unreachable!("clippy::lint_args can only be called from `clippy` subcommands.");
32 };
33
34 let mut args = vec![];
35 if *fix {
36 #[rustfmt::skip]
37 args.extend(strings(&[
38 "--fix", "-Zunstable-options",
39 "--lib", "--bins", "--examples",
43 ]));
44
45 if *allow_dirty {
46 args.push("--allow-dirty".to_owned());
47 }
48
49 if *allow_staged {
50 args.push("--allow-staged".to_owned());
51 }
52 }
53
54 args.extend(strings(&["--"]));
55
56 if config.deny.is_empty() && config.forbid.is_empty() {
57 args.extend(strings(&["--cap-lints", "warn"]));
58 }
59
60 let all_args = std::env::args().collect::<Vec<_>>();
61 args.extend(get_clippy_rules_in_order(&all_args, config));
62
63 args.extend(ignored_rules.iter().map(|lint| format!("-Aclippy::{lint}")));
64 args.extend(builder.config.free_args.clone());
65 args
66}
67
68pub fn get_clippy_rules_in_order(all_args: &[String], config: &LintConfig) -> Vec<String> {
72 let mut result = vec![];
73
74 for (prefix, item) in
75 [("-A", &config.allow), ("-D", &config.deny), ("-W", &config.warn), ("-F", &config.forbid)]
76 {
77 item.iter().for_each(|v| {
78 let rule = format!("{prefix}{v}");
79 let position = all_args.iter().position(|t| t == &rule || t == v).unwrap_or(usize::MAX);
82 result.push((position, rule));
83 });
84 }
85
86 result.sort_by_key(|&(position, _)| position);
87 result.into_iter().map(|v| v.1).collect()
88}
89
90#[derive(Debug, Clone, PartialEq, Eq, Hash)]
91pub struct LintConfig {
92 pub allow: Vec<String>,
93 pub warn: Vec<String>,
94 pub deny: Vec<String>,
95 pub forbid: Vec<String>,
96}
97
98impl LintConfig {
99 fn new(builder: &Builder<'_>) -> Self {
100 match builder.config.cmd.clone() {
101 Subcommand::Clippy { allow, deny, warn, forbid, .. } => {
102 Self { allow, warn, deny, forbid }
103 }
104 _ => unreachable!("LintConfig can only be called from `clippy` subcommands."),
105 }
106 }
107
108 fn merge(&self, other: &Self) -> Self {
109 let merged = |self_attr: &[String], other_attr: &[String]| -> Vec<String> {
110 self_attr.iter().cloned().chain(other_attr.iter().cloned()).collect()
111 };
112 Self {
114 allow: merged(&self.allow, &other.allow),
115 warn: merged(&self.warn, &other.warn),
116 deny: merged(&self.deny, &other.deny),
117 forbid: merged(&self.forbid, &other.forbid),
118 }
119 }
120}
121
122#[derive(Debug, Clone, PartialEq, Eq, Hash)]
123pub struct Std {
124 pub target: TargetSelection,
125 config: LintConfig,
126 crates: Vec<String>,
128}
129
130impl Step for Std {
131 type Output = ();
132 const DEFAULT: bool = true;
133
134 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
135 run.crate_or_deps("sysroot").path("library")
136 }
137
138 fn make_run(run: RunConfig<'_>) {
139 let crates = std_crates_for_run_make(&run);
140 let config = LintConfig::new(run.builder);
141 run.builder.ensure(Std { target: run.target, config, crates });
142 }
143
144 fn run(self, builder: &Builder<'_>) {
145 let target = self.target;
146 let compiler = builder.compiler(builder.top_stage, builder.config.host_target);
147
148 let mut cargo = builder::Cargo::new(
149 builder,
150 compiler,
151 Mode::Std,
152 SourceType::InTree,
153 target,
154 Kind::Clippy,
155 );
156
157 std_cargo(builder, target, compiler.stage, &mut cargo);
158
159 for krate in &*self.crates {
160 cargo.arg("-p").arg(krate);
161 }
162
163 let _guard =
164 builder.msg_clippy(format_args!("library{}", crate_description(&self.crates)), target);
165
166 run_cargo(
167 builder,
168 cargo,
169 lint_args(builder, &self.config, IGNORED_RULES_FOR_STD_AND_RUSTC),
170 &build_stamp::libstd_stamp(builder, compiler, target),
171 vec![],
172 true,
173 false,
174 );
175 }
176}
177
178#[derive(Debug, Clone, PartialEq, Eq, Hash)]
179pub struct Rustc {
180 pub target: TargetSelection,
181 config: LintConfig,
182 crates: Vec<String>,
184}
185
186impl Step for Rustc {
187 type Output = ();
188 const ONLY_HOSTS: bool = true;
189 const DEFAULT: bool = true;
190
191 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
192 run.crate_or_deps("rustc-main").path("compiler")
193 }
194
195 fn make_run(run: RunConfig<'_>) {
196 let crates = run.make_run_crates(Alias::Compiler);
197 let config = LintConfig::new(run.builder);
198 run.builder.ensure(Rustc { target: run.target, config, crates });
199 }
200
201 fn run(self, builder: &Builder<'_>) {
206 let compiler = builder.compiler(builder.top_stage, builder.config.host_target);
207 let target = self.target;
208
209 if !builder.download_rustc() {
210 if compiler.stage != 0 {
211 builder.std(compiler, compiler.host);
217 builder.std(compiler, target);
218 } else {
219 builder.ensure(check::Std::new(compiler, target));
220 }
221 }
222
223 let mut cargo = builder::Cargo::new(
224 builder,
225 compiler,
226 Mode::Rustc,
227 SourceType::InTree,
228 target,
229 Kind::Clippy,
230 );
231
232 rustc_cargo(builder, &mut cargo, target, &compiler, &self.crates);
233
234 for krate in &*self.crates {
238 cargo.arg("-p").arg(krate);
239 }
240
241 let _guard =
242 builder.msg_clippy(format_args!("compiler{}", crate_description(&self.crates)), target);
243
244 run_cargo(
245 builder,
246 cargo,
247 lint_args(builder, &self.config, IGNORED_RULES_FOR_STD_AND_RUSTC),
248 &build_stamp::librustc_stamp(builder, compiler, target),
249 vec![],
250 true,
251 false,
252 );
253 }
254}
255
256macro_rules! lint_any {
257 ($(
258 $name:ident, $path:expr, $readable_name:expr
259 $(,lint_by_default = $lint_by_default:expr)*
260 ;
261 )+) => {
262 $(
263
264 #[derive(Debug, Clone, Hash, PartialEq, Eq)]
265 pub struct $name {
266 pub target: TargetSelection,
267 config: LintConfig,
268 }
269
270 impl Step for $name {
271 type Output = ();
272 const DEFAULT: bool = if false $(|| $lint_by_default)* { true } else { false };
273
274 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
275 run.path($path)
276 }
277
278 fn make_run(run: RunConfig<'_>) {
279 let config = LintConfig::new(run.builder);
280 run.builder.ensure($name {
281 target: run.target,
282 config,
283 });
284 }
285
286 fn run(self, builder: &Builder<'_>) -> Self::Output {
287 let compiler = builder.compiler(builder.top_stage, builder.config.host_target);
288 let target = self.target;
289
290 if !builder.download_rustc() {
291 builder.ensure(check::Rustc::new(builder, compiler, target));
292 };
293
294 let cargo = prepare_tool_cargo(
295 builder,
296 compiler,
297 Mode::ToolRustc,
298 target,
299 Kind::Clippy,
300 $path,
301 SourceType::InTree,
302 &[],
303 );
304
305 let _guard = builder.msg_tool(
306 Kind::Clippy,
307 Mode::ToolRustc,
308 $readable_name,
309 compiler.stage,
310 &compiler.host,
311 &target,
312 );
313
314 let stringified_name = stringify!($name).to_lowercase();
315 let stamp = BuildStamp::new(&builder.cargo_out(compiler, Mode::ToolRustc, target))
316 .with_prefix(&format!("{}-check", stringified_name));
317
318 run_cargo(
319 builder,
320 cargo,
321 lint_args(builder, &self.config, &[]),
322 &stamp,
323 vec![],
324 true,
325 false,
326 );
327 }
328 }
329 )+
330 }
331}
332
333lint_any!(
334 Bootstrap, "src/bootstrap", "bootstrap";
335 BuildHelper, "src/build_helper", "build_helper";
336 BuildManifest, "src/tools/build-manifest", "build-manifest";
337 CargoMiri, "src/tools/miri/cargo-miri", "cargo-miri";
338 Clippy, "src/tools/clippy", "clippy";
339 CollectLicenseMetadata, "src/tools/collect-license-metadata", "collect-license-metadata";
340 CodegenGcc, "compiler/rustc_codegen_gcc", "rustc-codegen-gcc";
341 Compiletest, "src/tools/compiletest", "compiletest";
342 CoverageDump, "src/tools/coverage-dump", "coverage-dump";
343 Jsondocck, "src/tools/jsondocck", "jsondocck";
344 Jsondoclint, "src/tools/jsondoclint", "jsondoclint";
345 LintDocs, "src/tools/lint-docs", "lint-docs";
346 LlvmBitcodeLinker, "src/tools/llvm-bitcode-linker", "llvm-bitcode-linker";
347 Miri, "src/tools/miri", "miri";
348 MiroptTestTools, "src/tools/miropt-test-tools", "miropt-test-tools";
349 OptDist, "src/tools/opt-dist", "opt-dist";
350 RemoteTestClient, "src/tools/remote-test-client", "remote-test-client";
351 RemoteTestServer, "src/tools/remote-test-server", "remote-test-server";
352 RustAnalyzer, "src/tools/rust-analyzer", "rust-analyzer";
353 Rustdoc, "src/librustdoc", "clippy";
354 Rustfmt, "src/tools/rustfmt", "rustfmt";
355 RustInstaller, "src/tools/rust-installer", "rust-installer";
356 Tidy, "src/tools/tidy", "tidy";
357 TestFloatParse, "src/tools/test-float-parse", "test-float-parse";
358);
359
360#[derive(Debug, Clone, PartialEq, Eq, Hash)]
361pub struct CI {
362 target: TargetSelection,
363 config: LintConfig,
364}
365
366impl Step for CI {
367 type Output = ();
368 const DEFAULT: bool = false;
369
370 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
371 run.alias("ci")
372 }
373
374 fn make_run(run: RunConfig<'_>) {
375 let config = LintConfig::new(run.builder);
376 run.builder.ensure(CI { target: run.target, config });
377 }
378
379 fn run(self, builder: &Builder<'_>) -> Self::Output {
380 builder.ensure(Bootstrap {
381 target: self.target,
382 config: self.config.merge(&LintConfig {
383 allow: vec![],
384 warn: vec![],
385 deny: vec!["warnings".into()],
386 forbid: vec![],
387 }),
388 });
389 let library_clippy_cfg = LintConfig {
390 allow: vec!["clippy::all".into()],
391 warn: vec![],
392 deny: vec![
393 "clippy::correctness".into(),
394 "clippy::char_lit_as_u8".into(),
395 "clippy::four_forward_slashes".into(),
396 "clippy::needless_bool".into(),
397 "clippy::needless_bool_assign".into(),
398 "clippy::non_minimal_cfg".into(),
399 "clippy::print_literal".into(),
400 "clippy::same_item_push".into(),
401 "clippy::single_char_add_str".into(),
402 "clippy::to_string_in_format_args".into(),
403 ],
404 forbid: vec![],
405 };
406 builder.ensure(Std {
407 target: self.target,
408 config: self.config.merge(&library_clippy_cfg),
409 crates: vec![],
410 });
411
412 let compiler_clippy_cfg = LintConfig {
413 allow: vec!["clippy::all".into()],
414 warn: vec![],
415 deny: vec![
416 "clippy::correctness".into(),
417 "clippy::char_lit_as_u8".into(),
418 "clippy::clone_on_ref_ptr".into(),
419 "clippy::format_in_format_args".into(),
420 "clippy::four_forward_slashes".into(),
421 "clippy::needless_bool".into(),
422 "clippy::needless_bool_assign".into(),
423 "clippy::non_minimal_cfg".into(),
424 "clippy::print_literal".into(),
425 "clippy::same_item_push".into(),
426 "clippy::single_char_add_str".into(),
427 "clippy::to_string_in_format_args".into(),
428 ],
429 forbid: vec![],
430 };
431 builder.ensure(Rustc {
432 target: self.target,
433 config: self.config.merge(&compiler_clippy_cfg),
434 crates: vec![],
435 });
436
437 let rustc_codegen_gcc = LintConfig {
438 allow: vec![],
439 warn: vec![],
440 deny: vec!["warnings".into()],
441 forbid: vec![],
442 };
443 builder.ensure(CodegenGcc {
444 target: self.target,
445 config: self.config.merge(&rustc_codegen_gcc),
446 });
447 }
448}