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 compiler.stage != 0 {
211 builder.ensure(compile::Std::new(compiler, compiler.host));
217 builder.ensure(compile::Std::new(compiler, target));
218 } else {
219 builder.ensure(check::Std::new(target).build_kind(Some(Kind::Check)));
220 }
221
222 let mut cargo = builder::Cargo::new(
223 builder,
224 compiler,
225 Mode::Rustc,
226 SourceType::InTree,
227 target,
228 Kind::Clippy,
229 );
230
231 rustc_cargo(builder, &mut cargo, target, &compiler, &self.crates);
232
233 for krate in &*self.crates {
237 cargo.arg("-p").arg(krate);
238 }
239
240 let _guard =
241 builder.msg_clippy(format_args!("compiler{}", crate_description(&self.crates)), target);
242
243 run_cargo(
244 builder,
245 cargo,
246 lint_args(builder, &self.config, IGNORED_RULES_FOR_STD_AND_RUSTC),
247 &build_stamp::librustc_stamp(builder, compiler, target),
248 vec![],
249 true,
250 false,
251 );
252 }
253}
254
255macro_rules! lint_any {
256 ($(
257 $name:ident, $path:expr, $readable_name:expr
258 $(,lint_by_default = $lint_by_default:expr)*
259 ;
260 )+) => {
261 $(
262
263 #[derive(Debug, Clone, Hash, PartialEq, Eq)]
264 pub struct $name {
265 pub target: TargetSelection,
266 config: LintConfig,
267 }
268
269 impl Step for $name {
270 type Output = ();
271 const DEFAULT: bool = if false $(|| $lint_by_default)* { true } else { false };
272
273 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
274 run.path($path)
275 }
276
277 fn make_run(run: RunConfig<'_>) {
278 let config = LintConfig::new(run.builder);
279 run.builder.ensure($name {
280 target: run.target,
281 config,
282 });
283 }
284
285 fn run(self, builder: &Builder<'_>) -> Self::Output {
286 let compiler = builder.compiler(builder.top_stage, builder.config.build);
287 let target = self.target;
288
289 builder.ensure(check::Rustc::new(target, builder).build_kind(Some(Kind::Check)));
290
291 let cargo = prepare_tool_cargo(
292 builder,
293 compiler,
294 Mode::ToolRustc,
295 target,
296 Kind::Clippy,
297 $path,
298 SourceType::InTree,
299 &[],
300 );
301
302 let _guard = builder.msg_tool(
303 Kind::Clippy,
304 Mode::ToolRustc,
305 $readable_name,
306 compiler.stage,
307 &compiler.host,
308 &target,
309 );
310
311 let stringified_name = stringify!($name).to_lowercase();
312 let stamp = BuildStamp::new(&builder.cargo_out(compiler, Mode::ToolRustc, target))
313 .with_prefix(&format!("{}-check", stringified_name));
314
315 run_cargo(
316 builder,
317 cargo,
318 lint_args(builder, &self.config, &[]),
319 &stamp,
320 vec![],
321 true,
322 false,
323 );
324 }
325 }
326 )+
327 }
328}
329
330lint_any!(
331 Bootstrap, "src/bootstrap", "bootstrap";
332 BuildHelper, "src/build_helper", "build_helper";
333 BuildManifest, "src/tools/build-manifest", "build-manifest";
334 CargoMiri, "src/tools/miri/cargo-miri", "cargo-miri";
335 Clippy, "src/tools/clippy", "clippy";
336 CollectLicenseMetadata, "src/tools/collect-license-metadata", "collect-license-metadata";
337 CodegenGcc, "compiler/rustc_codegen_gcc", "rustc-codegen-gcc";
338 Compiletest, "src/tools/compiletest", "compiletest";
339 CoverageDump, "src/tools/coverage-dump", "coverage-dump";
340 Jsondocck, "src/tools/jsondocck", "jsondocck";
341 Jsondoclint, "src/tools/jsondoclint", "jsondoclint";
342 LintDocs, "src/tools/lint-docs", "lint-docs";
343 LlvmBitcodeLinker, "src/tools/llvm-bitcode-linker", "llvm-bitcode-linker";
344 Miri, "src/tools/miri", "miri";
345 MiroptTestTools, "src/tools/miropt-test-tools", "miropt-test-tools";
346 OptDist, "src/tools/opt-dist", "opt-dist";
347 RemoteTestClient, "src/tools/remote-test-client", "remote-test-client";
348 RemoteTestServer, "src/tools/remote-test-server", "remote-test-server";
349 Rls, "src/tools/rls", "rls";
350 RustAnalyzer, "src/tools/rust-analyzer", "rust-analyzer";
351 Rustdoc, "src/librustdoc", "clippy";
352 Rustfmt, "src/tools/rustfmt", "rustfmt";
353 RustInstaller, "src/tools/rust-installer", "rust-installer";
354 Tidy, "src/tools/tidy", "tidy";
355 TestFloatParse, "src/etc/test-float-parse", "test-float-parse";
356);
357
358#[derive(Debug, Clone, PartialEq, Eq, Hash)]
359pub struct CI {
360 target: TargetSelection,
361 config: LintConfig,
362}
363
364impl Step for CI {
365 type Output = ();
366 const DEFAULT: bool = false;
367
368 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
369 run.alias("ci")
370 }
371
372 fn make_run(run: RunConfig<'_>) {
373 let config = LintConfig::new(run.builder);
374 run.builder.ensure(CI { target: run.target, config });
375 }
376
377 fn run(self, builder: &Builder<'_>) -> Self::Output {
378 builder.ensure(Bootstrap {
379 target: self.target,
380 config: self.config.merge(&LintConfig {
381 allow: vec![],
382 warn: vec![],
383 deny: vec!["warnings".into()],
384 forbid: vec![],
385 }),
386 });
387 let library_clippy_cfg = LintConfig {
388 allow: vec!["clippy::all".into()],
389 warn: vec![],
390 deny: vec![
391 "clippy::correctness".into(),
392 "clippy::char_lit_as_u8".into(),
393 "clippy::four_forward_slashes".into(),
394 "clippy::needless_bool".into(),
395 "clippy::needless_bool_assign".into(),
396 "clippy::non_minimal_cfg".into(),
397 "clippy::print_literal".into(),
398 "clippy::same_item_push".into(),
399 "clippy::single_char_add_str".into(),
400 "clippy::to_string_in_format_args".into(),
401 ],
402 forbid: vec![],
403 };
404 builder.ensure(Std {
405 target: self.target,
406 config: self.config.merge(&library_clippy_cfg),
407 crates: vec![],
408 });
409
410 let compiler_clippy_cfg = LintConfig {
411 allow: vec!["clippy::all".into()],
412 warn: vec![],
413 deny: vec![
414 "clippy::correctness".into(),
415 "clippy::char_lit_as_u8".into(),
416 "clippy::clone_on_ref_ptr".into(),
417 "clippy::format_in_format_args".into(),
418 "clippy::four_forward_slashes".into(),
419 "clippy::needless_bool".into(),
420 "clippy::needless_bool_assign".into(),
421 "clippy::non_minimal_cfg".into(),
422 "clippy::print_literal".into(),
423 "clippy::same_item_push".into(),
424 "clippy::single_char_add_str".into(),
425 "clippy::to_string_in_format_args".into(),
426 ],
427 forbid: vec![],
428 };
429 builder.ensure(Rustc {
430 target: self.target,
431 config: self.config.merge(&compiler_clippy_cfg),
432 crates: vec![],
433 });
434
435 let rustc_codegen_gcc = LintConfig {
436 allow: vec![],
437 warn: vec![],
438 deny: vec!["warnings".into()],
439 forbid: vec![],
440 };
441 builder.ensure(CodegenGcc {
442 target: self.target,
443 config: self.config.merge(&rustc_codegen_gcc),
444 });
445 }
446}