Skip to main content

rust_tidy/
main.rs

1//! Tidy checks source code in this repository.
2//!
3//! This program runs all of the various tidy checks for style, cleanliness,
4//! etc. This is run by default on `./x.py test` and as part of the auto
5//! builders. The tidy checks can be executed with `./x.py test tidy`.
6
7use std::collections::VecDeque;
8use std::thread::{self, ScopedJoinHandle, scope};
9use std::{env, process};
10
11use tidy::arg_parser::TidyArgParser;
12use tidy::diagnostics::{COLOR_ERROR, COLOR_SUCCESS, TidyCtx, TidyFlags, output_message};
13use tidy::*;
14
15fn main() {
16    // Enable nightly, because Cargo will read the libstd Cargo.toml
17    // which uses the unstable `public-dependency` feature.
18    // SAFETY: no other threads have been spawned
19    unsafe {
20        env::set_var("RUSTC_BOOTSTRAP", "1");
21    }
22
23    let parsed_args = TidyArgParser::parse();
24
25    let root_path = parsed_args.root_path;
26    let cargo = parsed_args.cargo;
27    let output_directory = parsed_args.output_directory;
28    let concurrency = parsed_args.concurrency.get();
29    let npm = parsed_args.npm;
30
31    let root_manifest = root_path.join("Cargo.toml");
32    let typos_toml = root_path.join("typos.toml");
33    let src_path = root_path.join("src");
34    let tests_path = root_path.join("tests");
35    let library_path = root_path.join("library");
36    let compiler_path = root_path.join("compiler");
37    let librustdoc_path = src_path.join("librustdoc");
38    let tools_path = src_path.join("tools");
39    let crashes_path = tests_path.join("crashes");
40
41    let verbose = parsed_args.verbose;
42    let bless = parsed_args.bless;
43    let extra_checks = parsed_args.extra_checks;
44    let pos_args = parsed_args.pos_args;
45
46    let tidy_ctx = TidyCtx::new(&root_path, verbose, TidyFlags::new(bless));
47    let ci_info = CiInfo::new(tidy_ctx.clone());
48
49    let drain_handles = |handles: &mut VecDeque<ScopedJoinHandle<'_, ()>>| {
50        // poll all threads for completion before awaiting the oldest one
51        for i in (0..handles.len()).rev() {
52            if handles[i].is_finished() {
53                handles.swap_remove_back(i).unwrap().join().unwrap();
54            }
55        }
56
57        while handles.len() >= concurrency {
58            handles.pop_front().unwrap().join().unwrap();
59        }
60    };
61
62    scope(|s| {
63        let mut handles: VecDeque<ScopedJoinHandle<'_, ()>> = VecDeque::with_capacity(concurrency);
64
65        macro_rules! check {
66            ($p:ident) => {
67                check!(@ $p, name=format!("{}", stringify!($p)));
68            };
69            ($p:ident, $path:expr $(, $args:expr)* ) => {
70                let shortened = $path.strip_prefix(&root_path).unwrap();
71                let name = if shortened == std::path::Path::new("") {
72                    format!("{} (.)", stringify!($p))
73                } else {
74                    format!("{} ({})", stringify!($p), shortened.display())
75                };
76                check!(@ $p, name=name, $path $(,$args)*);
77            };
78            (@ $p:ident, name=$name:expr $(, $args:expr)* ) => {
79                drain_handles(&mut handles);
80
81                let tidy_ctx = tidy_ctx.clone();
82                let handle = thread::Builder::new().name($name).spawn_scoped(s, || {
83                    $p::check($($args, )* tidy_ctx);
84                }).unwrap();
85                handles.push_back(handle);
86            }
87        }
88
89        check!(target_specific_tests, &tests_path);
90
91        // Checks that are done on the cargo workspace.
92        check!(deps, &root_path, &cargo);
93        check!(extdeps, &root_path);
94
95        // Checks over tests.
96        check!(tests_placement, &root_path);
97        check!(tests_revision_unpaired_stdout_stderr, &tests_path);
98        check!(debug_artifacts, &tests_path);
99        check!(ui_tests, &root_path);
100        check!(mir_opt_tests, &tests_path);
101        check!(rustdoc_gui_tests, &tests_path);
102        check!(rustdoc_css_themes, &librustdoc_path);
103        check!(rustdoc_templates, &librustdoc_path);
104        check!(rustdoc_json, &src_path, &ci_info);
105        check!(known_bug, &crashes_path);
106        check!(unknown_revision, &tests_path);
107
108        // Checks that only make sense for the compiler.
109        check!(error_codes, &root_path, &[&compiler_path, &librustdoc_path], &ci_info);
110        check!(fluent_alphabetical, &compiler_path);
111        check!(fluent_period, &compiler_path);
112        check!(fluent_lowercase, &compiler_path);
113        check!(target_policy, &root_path);
114        check!(gcc_submodule, &root_path, &compiler_path);
115
116        // Checks that only make sense for the std libs.
117        check!(pal, &library_path);
118
119        // Checks that need to be done for both the compiler and std libraries.
120        check!(unit_tests, &src_path, false);
121        check!(unit_tests, &compiler_path, false);
122        check!(unit_tests, &library_path, true);
123
124        if bins::check_filesystem_support(&[&root_path], &output_directory) {
125            check!(bins, &root_path);
126        }
127
128        check!(style, &src_path);
129        check!(style, &tests_path);
130        check!(style, &compiler_path);
131        check!(style, &library_path);
132
133        check!(edition, &src_path);
134        check!(edition, &compiler_path);
135        check!(edition, &library_path);
136
137        check!(alphabetical, &root_manifest);
138        check!(alphabetical, &typos_toml);
139        check!(alphabetical, &src_path);
140        check!(alphabetical, &tests_path);
141        check!(alphabetical, &compiler_path);
142        check!(alphabetical, &library_path);
143
144        check!(x_version, &root_path, &cargo);
145
146        check!(triagebot, &root_path);
147        check!(filenames, &root_path);
148
149        let collected = {
150            drain_handles(&mut handles);
151
152            features::check(&src_path, &tests_path, &compiler_path, &library_path, tidy_ctx.clone())
153        };
154        check!(unstable_book, &src_path, collected);
155
156        check!(
157            extra_checks,
158            &root_path,
159            &output_directory,
160            &ci_info,
161            &librustdoc_path,
162            &tools_path,
163            &npm,
164            &cargo,
165            extra_checks,
166            pos_args
167        );
168    });
169
170    let failed_checks = tidy_ctx.into_failed_checks();
171    if !failed_checks.is_empty() {
172        let mut failed: Vec<String> =
173            failed_checks.into_iter().map(|c| c.id().to_string()).collect();
174        failed.sort();
175        output_message(
176            &format!(
177                "The following check{} failed: {}",
178                if failed.len() > 1 { "s" } else { "" },
179                failed.join(", ")
180            ),
181            None,
182            Some(COLOR_ERROR),
183        );
184        process::exit(1);
185    } else {
186        output_message("All tidy checks succeeded", None, Some(COLOR_SUCCESS));
187    }
188}