tidy/
lib.rs

1//! Library used by tidy and other tools.
2//!
3//! This library contains the tidy lints and exposes it
4//! to be used by tools.
5
6use std::ffi::OsStr;
7use std::process::Command;
8
9use build_helper::ci::CiEnv;
10use build_helper::git::{GitConfig, get_closest_upstream_commit};
11use build_helper::stage0_parser::{Stage0Config, parse_stage0_file};
12use termcolor::WriteColor;
13
14macro_rules! static_regex {
15    ($re:literal) => {{
16        static RE: ::std::sync::OnceLock<::regex::Regex> = ::std::sync::OnceLock::new();
17        RE.get_or_init(|| ::regex::Regex::new($re).unwrap())
18    }};
19}
20
21/// A helper macro to `unwrap` a result except also print out details like:
22///
23/// * The expression that failed
24/// * The error itself
25/// * (optionally) a path connected to the error (e.g. failure to open a file)
26#[macro_export]
27macro_rules! t {
28    ($e:expr, $p:expr) => {
29        match $e {
30            Ok(e) => e,
31            Err(e) => panic!("{} failed on {} with {}", stringify!($e), ($p).display(), e),
32        }
33    };
34
35    ($e:expr) => {
36        match $e {
37            Ok(e) => e,
38            Err(e) => panic!("{} failed with {}", stringify!($e), e),
39        }
40    };
41}
42
43macro_rules! tidy_error {
44    ($bad:expr, $($fmt:tt)*) => ({
45        $crate::tidy_error(&format_args!($($fmt)*).to_string()).expect("failed to output error");
46        *$bad = true;
47    });
48}
49
50macro_rules! tidy_error_ext {
51    ($tidy_error:path, $bad:expr, $($fmt:tt)*) => ({
52        $tidy_error(&format_args!($($fmt)*).to_string()).expect("failed to output error");
53        *$bad = true;
54    });
55}
56
57fn tidy_error(args: &str) -> std::io::Result<()> {
58    use std::io::Write;
59
60    use termcolor::{Color, ColorChoice, ColorSpec, StandardStream};
61
62    let mut stderr = StandardStream::stdout(ColorChoice::Auto);
63    stderr.set_color(ColorSpec::new().set_fg(Some(Color::Red)))?;
64
65    write!(&mut stderr, "tidy error")?;
66    stderr.set_color(&ColorSpec::new())?;
67
68    writeln!(&mut stderr, ": {args}")?;
69    Ok(())
70}
71
72pub struct CiInfo {
73    pub git_merge_commit_email: String,
74    pub nightly_branch: String,
75    pub base_commit: Option<String>,
76    pub ci_env: CiEnv,
77}
78
79impl CiInfo {
80    pub fn new(bad: &mut bool) -> Self {
81        let stage0 = parse_stage0_file();
82        let Stage0Config { nightly_branch, git_merge_commit_email, .. } = stage0.config;
83
84        let mut info = Self {
85            nightly_branch,
86            git_merge_commit_email,
87            ci_env: CiEnv::current(),
88            base_commit: None,
89        };
90        let base_commit = match get_closest_upstream_commit(None, &info.git_config(), info.ci_env) {
91            Ok(Some(commit)) => Some(commit),
92            Ok(None) => {
93                info.error_if_in_ci("no base commit found", bad);
94                None
95            }
96            Err(error) => {
97                info.error_if_in_ci(&format!("failed to retrieve base commit: {error}"), bad);
98                None
99            }
100        };
101        info.base_commit = base_commit;
102        info
103    }
104
105    pub fn git_config(&self) -> GitConfig<'_> {
106        GitConfig {
107            nightly_branch: &self.nightly_branch,
108            git_merge_commit_email: &self.git_merge_commit_email,
109        }
110    }
111
112    pub fn error_if_in_ci(&self, msg: &str, bad: &mut bool) {
113        if self.ci_env.is_running_in_ci() {
114            *bad = true;
115            eprintln!("tidy check error: {msg}");
116        } else {
117            eprintln!("tidy check warning: {msg}. Some checks will be skipped.");
118        }
119    }
120}
121
122pub fn git_diff<S: AsRef<OsStr>>(base_commit: &str, extra_arg: S) -> Option<String> {
123    let output = Command::new("git").arg("diff").arg(base_commit).arg(extra_arg).output().ok()?;
124    Some(String::from_utf8_lossy(&output.stdout).into())
125}
126
127pub mod alphabetical;
128pub mod bins;
129pub mod debug_artifacts;
130pub mod deps;
131pub mod edition;
132pub mod error_codes;
133pub mod ext_tool_checks;
134pub mod extdeps;
135pub mod features;
136pub mod fluent_alphabetical;
137pub mod fluent_period;
138mod fluent_used;
139pub mod gcc_submodule;
140pub(crate) mod iter_header;
141pub mod known_bug;
142pub mod mir_opt_tests;
143pub mod pal;
144pub mod rustdoc_css_themes;
145pub mod rustdoc_gui_tests;
146pub mod rustdoc_js;
147pub mod rustdoc_json;
148pub mod rustdoc_templates;
149pub mod style;
150pub mod target_policy;
151pub mod target_specific_tests;
152pub mod tests_placement;
153pub mod tests_revision_unpaired_stdout_stderr;
154pub mod triagebot;
155pub mod ui_tests;
156pub mod unit_tests;
157pub mod unknown_revision;
158pub mod unstable_book;
159pub mod walk;
160pub mod x_version;