Skip to main content

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
9macro_rules! static_regex {
10    ($re:literal) => {{
11        static RE: ::std::sync::LazyLock<::regex::Regex> =
12            ::std::sync::LazyLock::new(|| ::regex::Regex::new($re).unwrap());
13        &*RE
14    }};
15}
16
17/// A helper macro to `unwrap` a result except also print out details like:
18///
19/// * The expression that failed
20/// * The error itself
21/// * (optionally) a path connected to the error (e.g. failure to open a file)
22#[macro_export]
23macro_rules! t {
24    ($e:expr, $p:expr) => {
25        match $e {
26            Ok(e) => e,
27            Err(e) => panic!("{} failed on {} with {}", stringify!($e), ($p).display(), e),
28        }
29    };
30
31    ($e:expr) => {
32        match $e {
33            Ok(e) => e,
34            Err(e) => panic!("{} failed with {}", stringify!($e), e),
35        }
36    };
37}
38
39pub fn git_diff<S: AsRef<OsStr>>(base_commit: &str, extra_arg: S) -> Option<String> {
40    let output = Command::new("git").arg("diff").arg(base_commit).arg(extra_arg).output().ok()?;
41    Some(String::from_utf8_lossy(&output.stdout).into())
42}
43
44/// Similar to `files_modified`, but only involves a single call to `git`.
45///
46/// removes all elements from `items` that do not cause any match when `pred` is called with the list of modifed files.
47///
48/// if in CI, no elements will be removed.
49pub fn files_modified_batch_filter<T>(
50    base_commit: &Option<String>,
51    is_ci: bool,
52    items: &mut Vec<T>,
53    pred: impl Fn(&T, &str) -> bool,
54) {
55    if is_ci {
56        // assume everything is modified on CI because we really don't want false positives there.
57        return;
58    }
59    let Some(base_commit) = base_commit else {
60        eprintln!("No base commit, assuming all files are modified");
61        return;
62    };
63    match crate::git_diff(base_commit, "--name-status") {
64        Some(output) => {
65            let modified_files: Vec<_> = output
66                .lines()
67                .filter_map(|ln| {
68                    let (status, name) = ln
69                        .trim_end()
70                        .split_once('\t')
71                        .expect("bad format from `git diff --name-status`");
72                    if status == "M" { Some(name) } else { None }
73                })
74                .collect();
75            items.retain(|item| {
76                for modified_file in &modified_files {
77                    if pred(item, modified_file) {
78                        // at least one predicate matches, keep this item.
79                        return true;
80                    }
81                }
82                // no predicates matched, remove this item.
83                false
84            });
85        }
86        None => {
87            eprintln!("warning: failed to run `git diff` to check for changes");
88            eprintln!("warning: assuming all files are modified");
89        }
90    }
91}
92
93/// Returns true if any modified file matches the predicate, if we are in CI, or if unable to list modified files.
94pub fn files_modified(
95    base_commit: &Option<String>,
96    is_ci: bool,
97    pred: impl Fn(&str) -> bool,
98) -> bool {
99    let mut v = vec![()];
100    files_modified_batch_filter(base_commit, is_ci, &mut v, |_, p| pred(p));
101    !v.is_empty()
102}
103
104pub mod alphabetical;
105pub mod arg_parser;
106pub mod bins;
107pub mod debug_artifacts;
108pub mod deps;
109pub mod diagnostics;
110pub mod edition;
111pub mod error_codes;
112pub mod extdeps;
113pub mod extra_checks;
114pub mod features;
115pub mod filenames;
116pub mod gcc_submodule;
117pub(crate) mod iter_header;
118pub mod known_bug;
119pub mod mir_opt_tests;
120pub mod pal;
121pub mod rustdoc_css_themes;
122pub mod rustdoc_gui_tests;
123pub mod rustdoc_json;
124pub mod rustdoc_templates;
125pub mod style;
126pub mod target_policy;
127pub mod target_specific_tests;
128pub mod tests_placement;
129pub mod tests_revision_unpaired_stdout_stderr;
130pub mod triagebot;
131pub mod ui_tests;
132pub mod unit_tests;
133pub mod unknown_revision;
134pub mod unstable_book;
135pub mod walk;
136pub mod x_version;