diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index 342743d21340f..043fe7a72c50f 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -1,9 +1,9 @@ //! Checks the licenses of third-party dependencies. -use std::collections::{HashMap, HashSet}; +use std::collections::{BTreeSet, HashMap, HashSet}; use std::fmt::{Display, Formatter}; -use std::fs::{File, read_dir}; -use std::io::Write; +use std::fs::{self, read_dir}; +use std::io; use std::path::Path; use cargo_metadata::semver::Version; @@ -11,9 +11,6 @@ use cargo_metadata::{Metadata, Package, PackageId}; use crate::diagnostics::{RunningCheck, TidyCtx}; -#[path = "../../../bootstrap/src/utils/proc_macro_deps.rs"] -mod proc_macro_deps; - #[derive(Clone, Copy)] struct ListLocation { path: &'static str, @@ -722,53 +719,58 @@ fn check_proc_macro_dep_list(root: &Path, cargo: &Path, bless: bool, check: &mut } // Remove the proc-macro crates themselves proc_macro_deps.retain(|pkg| !is_proc_macro_pkg(&metadata[pkg])); + // Sort and deduplicate the crate names. + let proc_macro_deps = + proc_macro_deps.into_iter().map(|dep| metadata[dep].name.as_ref()).collect::>(); - let proc_macro_deps: HashSet<_> = - proc_macro_deps.into_iter().map(|dep| metadata[dep].name.as_ref()).collect(); - let expected = proc_macro_deps::CRATES.iter().copied().collect::>(); - - let needs_blessing = proc_macro_deps.difference(&expected).next().is_some() - || expected.difference(&proc_macro_deps).next().is_some(); + let expected = { + use std::fmt::Write; - if needs_blessing && bless { - let mut proc_macro_deps: Vec<_> = proc_macro_deps.into_iter().collect(); - proc_macro_deps.sort(); - let mut file = File::create(root.join("src/bootstrap/src/utils/proc_macro_deps.rs")) - .expect("`proc_macro_deps` should exist"); - writeln!( - &mut file, - "/// Do not update manually - use `./x.py test tidy --bless` + const HEADER: &str = "\ +/// Do not update manually - use `./x.py test tidy --bless` /// Holds all direct and indirect dependencies of proc-macro crates in tree. /// See pub static CRATES: &[&str] = &[ - // tidy-alphabetical-start" - ) - .unwrap(); + // tidy-alphabetical-start +"; + const FOOTER: &str = " // tidy-alphabetical-end +]; +"; + + let mut buf = String::with_capacity(4096); + buf.push_str(HEADER); for dep in proc_macro_deps { - writeln!(&mut file, " {dep:?},").unwrap(); + writeln!(buf, " {dep:?},").unwrap(); } - writeln!( - &mut file, - " // tidy-alphabetical-end -];" - ) - .unwrap(); - } else { - let mut error_found = false; + buf.push_str(FOOTER); + buf + }; - for missing in proc_macro_deps.difference(&expected) { - error_found = true; - check.error(format!( - "proc-macro crate dependency `{missing}` is not registered in `src/bootstrap/src/utils/proc_macro_deps.rs`", - )); - } - for extra in expected.difference(&proc_macro_deps) { - error_found = true; - check.error(format!( - "`{extra}` is registered in `src/bootstrap/src/utils/proc_macro_deps.rs`, but is not a proc-macro crate dependency", - )); + const PROC_MACRO_DEPS_RS: &str = "src/bootstrap/src/utils/proc_macro_deps.rs"; + let proc_macro_deps_rs_path = &root.join(PROC_MACRO_DEPS_RS); + let actual = match fs::read_to_string(proc_macro_deps_rs_path) { + Ok(actual) => actual, + Err(e) => { + if e.kind() == io::ErrorKind::NotFound { + check.error(format!( + "`{PROC_MACRO_DEPS_RS}` not found; has it been moved or renamed?" + )); + } else { + check.error(format!("`{PROC_MACRO_DEPS_RS}` could not be read: {e:?}")); + } + return; } - if error_found { + }; + + if actual != expected { + if bless { + fs::write(proc_macro_deps_rs_path, &expected).unwrap(); + } else { + let diff = similar::TextDiff::from_lines(&actual, &expected); + let mut unified = diff.unified_diff(); + unified.header(PROC_MACRO_DEPS_RS, "(expected)"); + + check.error(format!("`{PROC_MACRO_DEPS_RS}` is not up-to-date:\n{unified}")); check.message("Run `./x.py test tidy --bless` to regenerate the list"); } }