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