Skip to content

Commit df7da14

Browse files
authored
Rollup merge of #144511 - lolbinarycat:tidy-extra-checks-opt, r=Kobzol
tidy: increase performance of auto extra checks feature Removes the repeated calls to git diff. Halves the overhead of the tidy extra checks feature from 0.1 seconds to 0.05 on my machine, but probably will be more significant on systems on slow disks or less memory for i/o cache. r? ``@Kobzol``
2 parents 4bfbd80 + 5185a66 commit df7da14

File tree

2 files changed

+55
-30
lines changed

2 files changed

+55
-30
lines changed

src/tools/tidy/src/extra_checks/mod.rs

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ fn check_impl(
8686
std::env::var("TIDY_PRINT_DIFF").is_ok_and(|v| v.eq_ignore_ascii_case("true") || v == "1");
8787

8888
// Split comma-separated args up
89-
let lint_args = match extra_checks {
89+
let mut lint_args = match extra_checks {
9090
Some(s) => s
9191
.strip_prefix("--extra-checks=")
9292
.unwrap()
@@ -99,11 +99,7 @@ fn check_impl(
9999
})
100100
.filter_map(|(res, src)| match res {
101101
Ok(arg) => {
102-
if arg.is_inactive_auto(ci_info) {
103-
None
104-
} else {
105-
Some(arg)
106-
}
102+
Some(arg)
107103
}
108104
Err(err) => {
109105
// only warn because before bad extra checks would be silently ignored.
@@ -114,6 +110,11 @@ fn check_impl(
114110
.collect(),
115111
None => vec![],
116112
};
113+
if lint_args.iter().any(|ck| ck.auto) {
114+
crate::files_modified_batch_filter(ci_info, &mut lint_args, |ck, path| {
115+
ck.is_non_auto_or_matches(path)
116+
});
117+
}
117118

118119
macro_rules! extra_check {
119120
($lang:ident, $kind:ident) => {
@@ -721,23 +722,26 @@ impl ExtraCheckArg {
721722
self.lang == lang && self.kind.map(|k| k == kind).unwrap_or(true)
722723
}
723724

724-
/// Returns `true` if this is an auto arg and the relevant files are not modified.
725-
fn is_inactive_auto(&self, ci_info: &CiInfo) -> bool {
725+
/// Returns `false` if this is an auto arg and the passed filename does not trigger the auto rule
726+
fn is_non_auto_or_matches(&self, filepath: &str) -> bool {
726727
if !self.auto {
727-
return false;
728+
return true;
728729
}
729730
let ext = match self.lang {
730731
ExtraCheckLang::Py => ".py",
731732
ExtraCheckLang::Cpp => ".cpp",
732733
ExtraCheckLang::Shell => ".sh",
733734
ExtraCheckLang::Js => ".js",
734735
ExtraCheckLang::Spellcheck => {
735-
return !crate::files_modified(ci_info, |s| {
736-
SPELLCHECK_DIRS.iter().any(|dir| Path::new(s).starts_with(dir))
737-
});
736+
for dir in SPELLCHECK_DIRS {
737+
if Path::new(filepath).starts_with(dir) {
738+
return true;
739+
}
740+
}
741+
return false;
738742
}
739743
};
740-
!crate::files_modified(ci_info, |s| s.ends_with(ext))
744+
filepath.ends_with(ext)
741745
}
742746

743747
fn has_supported_kind(&self) -> bool {

src/tools/tidy/src/lib.rs

Lines changed: 38 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -125,40 +125,61 @@ pub fn git_diff<S: AsRef<OsStr>>(base_commit: &str, extra_arg: S) -> Option<Stri
125125
Some(String::from_utf8_lossy(&output.stdout).into())
126126
}
127127

128-
/// Returns true if any modified file matches the predicate, if we are in CI, or if unable to list modified files.
129-
pub fn files_modified(ci_info: &CiInfo, pred: impl Fn(&str) -> bool) -> bool {
128+
/// Similar to `files_modified`, but only involves a single call to `git`.
129+
///
130+
/// removes all elements from `items` that do not cause any match when `pred` is called with the list of modifed files.
131+
///
132+
/// if in CI, no elements will be removed.
133+
pub fn files_modified_batch_filter<T>(
134+
ci_info: &CiInfo,
135+
items: &mut Vec<T>,
136+
pred: impl Fn(&T, &str) -> bool,
137+
) {
130138
if CiEnv::is_ci() {
131139
// assume everything is modified on CI because we really don't want false positives there.
132-
return true;
140+
return;
133141
}
134142
let Some(base_commit) = &ci_info.base_commit else {
135143
eprintln!("No base commit, assuming all files are modified");
136-
return true;
144+
return;
137145
};
138146
match crate::git_diff(base_commit, "--name-status") {
139147
Some(output) => {
140-
let modified_files = output.lines().filter_map(|ln| {
141-
let (status, name) = ln
142-
.trim_end()
143-
.split_once('\t')
144-
.expect("bad format from `git diff --name-status`");
145-
if status == "M" { Some(name) } else { None }
146-
});
147-
for modified_file in modified_files {
148-
if pred(modified_file) {
149-
return true;
148+
let modified_files: Vec<_> = output
149+
.lines()
150+
.filter_map(|ln| {
151+
let (status, name) = ln
152+
.trim_end()
153+
.split_once('\t')
154+
.expect("bad format from `git diff --name-status`");
155+
if status == "M" { Some(name) } else { None }
156+
})
157+
.collect();
158+
items.retain(|item| {
159+
for modified_file in &modified_files {
160+
if pred(item, modified_file) {
161+
// at least one predicate matches, keep this item.
162+
return true;
163+
}
150164
}
151-
}
152-
false
165+
// no predicates matched, remove this item.
166+
false
167+
});
153168
}
154169
None => {
155170
eprintln!("warning: failed to run `git diff` to check for changes");
156171
eprintln!("warning: assuming all files are modified");
157-
true
158172
}
159173
}
160174
}
161175

176+
/// Returns true if any modified file matches the predicate, if we are in CI, or if unable to list modified files.
177+
pub fn files_modified(ci_info: &CiInfo, pred: impl Fn(&str) -> bool) -> bool {
178+
let mut v = vec![()];
179+
files_modified_batch_filter(ci_info, &mut v, |_, p| pred(p));
180+
!v.is_empty()
181+
}
182+
162183
pub mod alphabetical;
163184
pub mod bins;
164185
pub mod debug_artifacts;

0 commit comments

Comments
 (0)