summaryrefslogtreecommitdiffstats
path: root/src/tools/tidy
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/tidy')
-rw-r--r--src/tools/tidy/src/alphabetical.rs94
-rw-r--r--src/tools/tidy/src/alphabetical/tests.rs188
-rw-r--r--src/tools/tidy/src/deps.rs268
-rw-r--r--src/tools/tidy/src/extdeps.rs36
-rw-r--r--src/tools/tidy/src/features.rs16
-rw-r--r--src/tools/tidy/src/lib.rs16
-rw-r--r--src/tools/tidy/src/mir_opt_tests.rs3
-rw-r--r--src/tools/tidy/src/style.rs7
-rw-r--r--src/tools/tidy/src/ui_tests.rs2
9 files changed, 498 insertions, 132 deletions
diff --git a/src/tools/tidy/src/alphabetical.rs b/src/tools/tidy/src/alphabetical.rs
index fdc411c89..150a95943 100644
--- a/src/tools/tidy/src/alphabetical.rs
+++ b/src/tools/tidy/src/alphabetical.rs
@@ -1,6 +1,6 @@
//! Checks that a list of items is in alphabetical order
//!
-//! To use, use the following annotation in the code:
+//! Use the following marker in the code:
//! ```rust
//! // tidy-alphabetical-start
//! fn aaa() {}
@@ -10,17 +10,23 @@
//! ```
//!
//! The following lines are ignored:
+//! - Empty lines
//! - Lines that are indented with more or less spaces than the first line
-//! - Lines starting with `//`, `#[`, `)`, `]`, `}` if the comment has the same indentation as
-//! the first line
+//! - Lines starting with `//`, `#` (except those starting with `#!`), `)`, `]`, `}` if the comment
+//! has the same indentation as the first line
+//! - Lines starting with a closing delimiter (`)`, `[`, `}`) are ignored.
//!
-//! If a line ends with an opening bracket, the line is ignored and the next line will have
-//! its extra indentation ignored.
+//! If a line ends with an opening delimiter, we effectively join the following line to it before
+//! checking it. E.g. `foo(\nbar)` is treated like `foo(bar)`.
-use std::{fmt::Display, path::Path};
+use std::fmt::Display;
+use std::path::Path;
use crate::walk::{filter_dirs, walk};
+#[cfg(test)]
+mod tests;
+
fn indentation(line: &str) -> usize {
line.find(|c| c != ' ').unwrap_or(0)
}
@@ -29,28 +35,36 @@ fn is_close_bracket(c: char) -> bool {
matches!(c, ')' | ']' | '}')
}
-// Don't let tidy check this here :D
-const START_COMMENT: &str = concat!("// tidy-alphabetical", "-start");
-const END_COMMENT: &str = "// tidy-alphabetical-end";
+const START_MARKER: &str = "tidy-alphabetical-start";
+const END_MARKER: &str = "tidy-alphabetical-end";
fn check_section<'a>(
file: impl Display,
lines: impl Iterator<Item = (usize, &'a str)>,
+ err: &mut dyn FnMut(&str) -> std::io::Result<()>,
bad: &mut bool,
) {
- let content_lines = lines.take_while(|(_, line)| !line.contains(END_COMMENT));
-
let mut prev_line = String::new();
let mut first_indent = None;
let mut in_split_line = None;
- for (line_idx, line) in content_lines {
- if line.contains(START_COMMENT) {
- tidy_error!(
+ for (idx, line) in lines {
+ if line.is_empty() {
+ continue;
+ }
+
+ if line.contains(START_MARKER) {
+ tidy_error_ext!(
+ err,
bad,
- "{file}:{} found `{START_COMMENT}` expecting `{END_COMMENT}`",
- line_idx
- )
+ "{file}:{} found `{START_MARKER}` expecting `{END_MARKER}`",
+ idx + 1
+ );
+ return;
+ }
+
+ if line.contains(END_MARKER) {
+ return;
}
let indent = first_indent.unwrap_or_else(|| {
@@ -60,6 +74,7 @@ fn check_section<'a>(
});
let line = if let Some(prev_split_line) = in_split_line {
+ // Join the split lines.
in_split_line = None;
format!("{prev_split_line}{}", line.trim_start())
} else {
@@ -73,7 +88,7 @@ fn check_section<'a>(
let trimmed_line = line.trim_start_matches(' ');
if trimmed_line.starts_with("//")
- || trimmed_line.starts_with("#[")
+ || (trimmed_line.starts_with("#") && !trimmed_line.starts_with("#!"))
|| trimmed_line.starts_with(is_close_bracket)
{
continue;
@@ -87,25 +102,44 @@ fn check_section<'a>(
let prev_line_trimmed_lowercase = prev_line.trim_start_matches(' ').to_lowercase();
if trimmed_line.to_lowercase() < prev_line_trimmed_lowercase {
- tidy_error!(bad, "{file}:{}: line not in alphabetical order", line_idx + 1,);
+ tidy_error_ext!(err, bad, "{file}:{}: line not in alphabetical order", idx + 1);
}
prev_line = line;
}
+
+ tidy_error_ext!(err, bad, "{file}: reached end of file expecting `{END_MARKER}`")
}
-pub fn check(path: &Path, bad: &mut bool) {
- walk(path, |path, _is_dir| filter_dirs(path), &mut |entry, contents| {
- let file = &entry.path().display();
+fn check_lines<'a>(
+ file: &impl Display,
+ mut lines: impl Iterator<Item = (usize, &'a str)>,
+ err: &mut dyn FnMut(&str) -> std::io::Result<()>,
+ bad: &mut bool,
+) {
+ while let Some((idx, line)) = lines.next() {
+ if line.contains(END_MARKER) {
+ tidy_error_ext!(
+ err,
+ bad,
+ "{file}:{} found `{END_MARKER}` expecting `{START_MARKER}`",
+ idx + 1
+ )
+ }
- let mut lines = contents.lines().enumerate().peekable();
- while let Some((_, line)) = lines.next() {
- if line.contains(START_COMMENT) {
- check_section(file, &mut lines, bad);
- if lines.peek().is_none() {
- tidy_error!(bad, "{file}: reached end of file expecting `{END_COMMENT}`")
- }
- }
+ if line.contains(START_MARKER) {
+ check_section(file, &mut lines, err, bad);
}
+ }
+}
+
+pub fn check(path: &Path, bad: &mut bool) {
+ let skip =
+ |path: &_, _is_dir| filter_dirs(path) || path.ends_with("tidy/src/alphabetical/tests.rs");
+
+ walk(path, skip, &mut |entry, contents| {
+ let file = &entry.path().display();
+ let lines = contents.lines().enumerate();
+ check_lines(file, lines, &mut crate::tidy_error, bad)
});
}
diff --git a/src/tools/tidy/src/alphabetical/tests.rs b/src/tools/tidy/src/alphabetical/tests.rs
new file mode 100644
index 000000000..560e0284b
--- /dev/null
+++ b/src/tools/tidy/src/alphabetical/tests.rs
@@ -0,0 +1,188 @@
+use super::*;
+use std::io::Write;
+use std::str::from_utf8;
+
+fn test(lines: &str, name: &str, expected_msg: &str, expected_bad: bool) {
+ let mut actual_msg = Vec::new();
+ let mut actual_bad = false;
+ let mut err = |args: &_| {
+ write!(&mut actual_msg, "{args}")?;
+ Ok(())
+ };
+ check_lines(&name, lines.lines().enumerate(), &mut err, &mut actual_bad);
+ assert_eq!(expected_msg, from_utf8(&actual_msg).unwrap());
+ assert_eq!(expected_bad, actual_bad);
+}
+
+fn good(lines: &str) {
+ test(lines, "good", "", false);
+}
+
+fn bad(lines: &str, expected_msg: &str) {
+ test(lines, "bad", expected_msg, true);
+}
+
+#[test]
+fn test_no_markers() {
+ let lines = "\
+ def
+ abc
+ xyz
+ ";
+ good(lines);
+}
+
+#[test]
+fn test_rust_good() {
+ let lines = "\
+ // tidy-alphabetical-start
+ abc
+ def
+ xyz
+ // tidy-alphabetical-end"; // important: end marker on last line
+ good(lines);
+}
+
+#[test]
+fn test_complex_good() {
+ let lines = "\
+ zzz
+
+ // tidy-alphabetical-start
+ abc
+ // Rust comments are ok
+ def
+ # TOML comments are ok
+ xyz
+ // tidy-alphabetical-end
+
+ # tidy-alphabetical-start
+ foo(abc);
+ // blank lines are ok
+
+ // split line gets joined
+ foo(
+ def
+ );
+
+ foo(xyz);
+ # tidy-alphabetical-end
+
+ % tidy-alphabetical-start
+ abc
+ ignored_due_to_different_indent
+ def
+ % tidy-alphabetical-end
+
+ aaa
+ ";
+ good(lines);
+}
+
+#[test]
+fn test_rust_bad() {
+ let lines = "\
+ // tidy-alphabetical-start
+ abc
+ xyz
+ def
+ // tidy-alphabetical-end
+ ";
+ bad(lines, "bad:4: line not in alphabetical order");
+}
+
+#[test]
+fn test_toml_bad() {
+ let lines = "\
+ # tidy-alphabetical-start
+ abc
+ xyz
+ def
+ # tidy-alphabetical-end
+ ";
+ bad(lines, "bad:4: line not in alphabetical order");
+}
+
+#[test]
+fn test_features_bad() {
+ // Even though lines starting with `#` are treated as comments, lines
+ // starting with `#!` are an exception.
+ let lines = "\
+ tidy-alphabetical-start
+ #![feature(abc)]
+ #![feature(xyz)]
+ #![feature(def)]
+ tidy-alphabetical-end
+ ";
+ bad(lines, "bad:4: line not in alphabetical order");
+}
+
+#[test]
+fn test_indent_bad() {
+ // All lines are indented the same amount, and so are checked.
+ let lines = "\
+ $ tidy-alphabetical-start
+ abc
+ xyz
+ def
+ $ tidy-alphabetical-end
+ ";
+ bad(lines, "bad:4: line not in alphabetical order");
+}
+
+#[test]
+fn test_split_bad() {
+ let lines = "\
+ || tidy-alphabetical-start
+ foo(abc)
+ foo(
+ xyz
+ )
+ foo(
+ def
+ )
+ && tidy-alphabetical-end
+ ";
+ bad(lines, "bad:7: line not in alphabetical order");
+}
+
+#[test]
+fn test_double_start() {
+ let lines = "\
+ tidy-alphabetical-start
+ abc
+ tidy-alphabetical-start
+ ";
+ bad(lines, "bad:3 found `tidy-alphabetical-start` expecting `tidy-alphabetical-end`");
+}
+
+#[test]
+fn test_missing_start() {
+ let lines = "\
+ abc
+ tidy-alphabetical-end
+ abc
+ ";
+ bad(lines, "bad:2 found `tidy-alphabetical-end` expecting `tidy-alphabetical-start`");
+}
+
+#[test]
+fn test_missing_end() {
+ let lines = "\
+ tidy-alphabetical-start
+ abc
+ ";
+ bad(lines, "bad: reached end of file expecting `tidy-alphabetical-end`");
+}
+
+#[test]
+fn test_double_end() {
+ let lines = "\
+ tidy-alphabetical-start
+ abc
+ tidy-alphabetical-end
+ def
+ tidy-alphabetical-end
+ ";
+ bad(lines, "bad:5 found `tidy-alphabetical-end` expecting `tidy-alphabetical-start`");
+}
diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs
index 7d3ef4197..f88f91655 100644
--- a/src/tools/tidy/src/deps.rs
+++ b/src/tools/tidy/src/deps.rs
@@ -31,12 +31,49 @@ const LICENSES: &[&str] = &[
// tidy-alphabetical-end
];
+type ExceptionList = &'static [(&'static str, &'static str)];
+
+/// The workspaces to check for licensing and optionally permitted dependencies.
+///
+/// Each entry consists of a tuple with the following elements:
+///
+/// * The path to the workspace root Cargo.toml file.
+/// * The list of license exceptions.
+/// * Optionally a tuple of:
+/// * A list of crates for which dependencies need to be explicitly allowed.
+/// * The list of allowed dependencies.
+// FIXME auto detect all cargo workspaces
+pub(crate) const WORKSPACES: &[(&str, ExceptionList, Option<(&[&str], &[&str])>)] = &[
+ // The root workspace has to be first for check_rustfix to work.
+ (".", EXCEPTIONS, Some((&["rustc-main"], PERMITTED_RUSTC_DEPENDENCIES))),
+ // Outside of the alphabetical section because rustfmt formats it using multiple lines.
+ (
+ "compiler/rustc_codegen_cranelift",
+ EXCEPTIONS_CRANELIFT,
+ Some((&["rustc_codegen_cranelift"], PERMITTED_CRANELIFT_DEPENDENCIES)),
+ ),
+ // tidy-alphabetical-start
+ //("compiler/rustc_codegen_gcc", EXCEPTIONS_GCC, None), // FIXME uncomment once all deps are vendored
+ //("library/backtrace", &[], None), // FIXME uncomment once rust-lang/backtrace#562 has been synced back to the rust repo
+ //("library/portable-simd", &[], None), // FIXME uncomment once rust-lang/portable-simd#363 has been synced back to the rust repo
+ //("library/stdarch", EXCEPTIONS_STDARCH, None), // FIXME uncomment once rust-lang/stdarch#1462 has been synced back to the rust repo
+ ("src/bootstrap", EXCEPTIONS_BOOTSTRAP, None),
+ ("src/ci/docker/host-x86_64/test-various/uefi_qemu_test", EXCEPTIONS_UEFI_QEMU_TEST, None),
+ //("src/etc/test-float-parse", &[], None), // FIXME uncomment once all deps are vendored
+ ("src/tools/cargo", EXCEPTIONS_CARGO, None),
+ //("src/tools/miri/test-cargo-miri", &[], None), // FIXME uncomment once all deps are vendored
+ //("src/tools/miri/test_dependencies", &[], None), // FIXME uncomment once all deps are vendored
+ ("src/tools/rust-analyzer", EXCEPTIONS_RUST_ANALYZER, None),
+ ("src/tools/x", &[], None),
+ // tidy-alphabetical-end
+];
+
/// These are exceptions to Rust's permissive licensing policy, and
/// should be considered bugs. Exceptions are only allowed in Rust
/// tooling. It is _crucial_ that no exception crates be dependencies
/// of the Rust runtime (std/test).
#[rustfmt::skip]
-const EXCEPTIONS: &[(&str, &str)] = &[
+const EXCEPTIONS: ExceptionList = &[
// tidy-alphabetical-start
("ar_archive_writer", "Apache-2.0 WITH LLVM-exception"), // rustc
("colored", "MPL-2.0"), // rustfmt
@@ -47,18 +84,29 @@ const EXCEPTIONS: &[(&str, &str)] = &[
("instant", "BSD-3-Clause"), // rustc_driver/tracing-subscriber/parking_lot
("mdbook", "MPL-2.0"), // mdbook
("openssl", "Apache-2.0"), // opt-dist
+ ("option-ext", "MPL-2.0"), // cargo-miri (via `directories`)
("rustc_apfloat", "Apache-2.0 WITH LLVM-exception"), // rustc (license is the same as LLVM uses)
- ("ryu", "Apache-2.0 OR BSL-1.0"), // cargo/... (because of serde)
+ ("ryu", "Apache-2.0 OR BSL-1.0"), // BSL is not acceptble, but we use it under Apache-2.0 // cargo/... (because of serde)
("self_cell", "Apache-2.0"), // rustc (fluent translations)
("snap", "BSD-3-Clause"), // rustc
// tidy-alphabetical-end
];
-const EXCEPTIONS_CARGO: &[(&str, &str)] = &[
+// FIXME uncomment once rust-lang/stdarch#1462 lands
+/*
+const EXCEPTIONS_STDARCH: ExceptionList = &[
+ // tidy-alphabetical-start
+ ("ryu", "Apache-2.0 OR BSL-1.0"), // BSL is not acceptble, but we use it under Apache-2.0
+ ("wasmparser", "Apache-2.0 WITH LLVM-exception"),
+ ("wasmprinter", "Apache-2.0 WITH LLVM-exception"),
+ // tidy-alphabetical-end
+];
+*/
+
+const EXCEPTIONS_CARGO: ExceptionList = &[
// tidy-alphabetical-start
("bitmaps", "MPL-2.0+"),
("bytesize", "Apache-2.0"),
- ("byteyarn", "Apache-2.0"),
("ciborium", "Apache-2.0"),
("ciborium-io", "Apache-2.0"),
("ciborium-ll", "Apache-2.0"),
@@ -68,16 +116,30 @@ const EXCEPTIONS_CARGO: &[(&str, &str)] = &[
("im-rc", "MPL-2.0+"),
("normalize-line-endings", "Apache-2.0"),
("openssl", "Apache-2.0"),
- ("ryu", "Apache-2.0 OR BSL-1.0"),
+ ("ryu", "Apache-2.0 OR BSL-1.0"), // BSL is not acceptble, but we use it under Apache-2.0
("sha1_smol", "BSD-3-Clause"),
("similar", "Apache-2.0"),
("sized-chunks", "MPL-2.0+"),
("subtle", "BSD-3-Clause"),
+ ("supports-hyperlinks", "Apache-2.0"),
("unicode-bom", "Apache-2.0"),
// tidy-alphabetical-end
];
-const EXCEPTIONS_CRANELIFT: &[(&str, &str)] = &[
+const EXCEPTIONS_RUST_ANALYZER: ExceptionList = &[
+ // tidy-alphabetical-start
+ ("anymap", "BlueOak-1.0.0 OR MIT OR Apache-2.0"), // BlueOak is not acceptable, but we use it under MIT OR Apache-2 .0
+ ("dissimilar", "Apache-2.0"),
+ ("instant", "BSD-3-Clause"),
+ ("notify", "CC0-1.0"),
+ ("pulldown-cmark-to-cmark", "Apache-2.0"),
+ ("ryu", "Apache-2.0 OR BSL-1.0"), // BSL is not acceptble, but we use it under Apache-2.0
+ ("scip", "Apache-2.0"),
+ ("snap", "BSD-3-Clause"),
+ // tidy-alphabetical-end
+];
+
+const EXCEPTIONS_CRANELIFT: ExceptionList = &[
// tidy-alphabetical-start
("cranelift-bforest", "Apache-2.0 WITH LLVM-exception"),
("cranelift-codegen", "Apache-2.0 WITH LLVM-exception"),
@@ -98,8 +160,22 @@ const EXCEPTIONS_CRANELIFT: &[(&str, &str)] = &[
// tidy-alphabetical-end
];
-const EXCEPTIONS_BOOTSTRAP: &[(&str, &str)] = &[
- ("ryu", "Apache-2.0 OR BSL-1.0"), // through serde
+// FIXME uncomment once all deps are vendored
+/*
+const EXCEPTIONS_GCC: ExceptionList = &[
+ // tidy-alphabetical-start
+ ("gccjit", "GPL-3.0"),
+ ("gccjit_sys", "GPL-3.0"),
+ // tidy-alphabetical-end
+];
+*/
+
+const EXCEPTIONS_BOOTSTRAP: ExceptionList = &[
+ ("ryu", "Apache-2.0 OR BSL-1.0"), // through serde. BSL is not acceptble, but we use it under Apache-2.0
+];
+
+const EXCEPTIONS_UEFI_QEMU_TEST: ExceptionList = &[
+ ("r-efi", "MIT OR Apache-2.0 OR LGPL-2.1-or-later"), // LGPL is not acceptible, but we use it under MIT OR Apache-2.0
];
/// These are the root crates that are part of the runtime. The licenses for
@@ -141,6 +217,7 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
"darling_core",
"darling_macro",
"datafrog",
+ "derivative",
"derive_more",
"derive_setters",
"digest",
@@ -152,7 +229,6 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
"ena",
"equivalent",
"errno",
- "errno-dragonfly",
"expect-test",
"fallible-iterator", // dependency of `thorin`
"fastrand",
@@ -171,7 +247,10 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
"hashbrown",
"hermit-abi",
"icu_list",
+ "icu_list_data",
"icu_locid",
+ "icu_locid_transform",
+ "icu_locid_transform_data",
"icu_provider",
"icu_provider_adapters",
"icu_provider_macros",
@@ -183,6 +262,7 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
"is-terminal",
"itertools",
"itoa",
+ "jemalloc-sys",
"jobserver",
"lazy_static",
"libc",
@@ -210,6 +290,7 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
"perf-event-open-sys",
"pin-project-lite",
"polonius-engine",
+ "portable-atomic", // dependency for platforms doesn't support `AtomicU64` in std
"ppv-lite86",
"proc-macro-hack",
"proc-macro2",
@@ -320,12 +401,37 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
// tidy-alphabetical-end
];
+// These crates come from ICU4X and are licensed under the unicode license.
+// It currently doesn't have an SPDX identifier, so they cannot put one there.
+// See https://github.com/unicode-org/icu4x/pull/3875
+// FIXME: This should be removed once ICU4X crates update.
+const ICU4X_UNICODE_LICENSE_DEPENDENCIES: &[&str] = &[
+ // tidy-alphabetical-start
+ "icu_list",
+ "icu_list_data",
+ "icu_locid",
+ "icu_locid_transform",
+ "icu_locid_transform_data",
+ "icu_provider",
+ "icu_provider_adapters",
+ "icu_provider_macros",
+ "litemap",
+ "tinystr",
+ "writeable",
+ "yoke",
+ "yoke-derive",
+ "zerofrom",
+ "zerofrom-derive",
+ "zerovec",
+ "zerovec-derive",
+ // tidy-alphabetical-end
+];
+
const PERMITTED_CRANELIFT_DEPENDENCIES: &[&str] = &[
// tidy-alphabetical-start
"ahash",
"anyhow",
"arbitrary",
- "autocfg",
"bitflags",
"bumpalo",
"cfg-if",
@@ -382,65 +488,90 @@ const PERMITTED_CRANELIFT_DEPENDENCIES: &[&str] = &[
/// `root` is path to the directory with the root `Cargo.toml` (for the workspace). `cargo` is path
/// to the cargo executable.
pub fn check(root: &Path, cargo: &Path, bad: &mut bool) {
- let mut cmd = cargo_metadata::MetadataCommand::new();
- cmd.cargo_path(cargo)
- .manifest_path(root.join("Cargo.toml"))
- .features(cargo_metadata::CargoOpt::AllFeatures);
- let metadata = t!(cmd.exec());
- let runtime_ids = compute_runtime_crates(&metadata);
- check_license_exceptions(&metadata, EXCEPTIONS, runtime_ids, bad);
- check_permitted_dependencies(
- &metadata,
- "rustc",
- PERMITTED_RUSTC_DEPENDENCIES,
- &["rustc_driver", "rustc_codegen_llvm"],
- bad,
- );
-
- // Check cargo independently as it has it's own workspace.
- let mut cmd = cargo_metadata::MetadataCommand::new();
- cmd.cargo_path(cargo)
- .manifest_path(root.join("src/tools/cargo/Cargo.toml"))
- .features(cargo_metadata::CargoOpt::AllFeatures);
- let cargo_metadata = t!(cmd.exec());
- let runtime_ids = HashSet::new();
- check_license_exceptions(&cargo_metadata, EXCEPTIONS_CARGO, runtime_ids, bad);
- check_rustfix(&metadata, &cargo_metadata, bad);
-
- // Check rustc_codegen_cranelift independently as it has it's own workspace.
- let mut cmd = cargo_metadata::MetadataCommand::new();
- cmd.cargo_path(cargo)
- .manifest_path(root.join("compiler/rustc_codegen_cranelift/Cargo.toml"))
- .features(cargo_metadata::CargoOpt::AllFeatures);
- let metadata = t!(cmd.exec());
- let runtime_ids = HashSet::new();
- check_license_exceptions(&metadata, EXCEPTIONS_CRANELIFT, runtime_ids, bad);
- check_permitted_dependencies(
- &metadata,
- "cranelift",
- PERMITTED_CRANELIFT_DEPENDENCIES,
- &["rustc_codegen_cranelift"],
- bad,
- );
-
- let mut cmd = cargo_metadata::MetadataCommand::new();
- cmd.cargo_path(cargo)
- .manifest_path(root.join("src/bootstrap/Cargo.toml"))
- .features(cargo_metadata::CargoOpt::AllFeatures);
- let metadata = t!(cmd.exec());
- let runtime_ids = HashSet::new();
- check_license_exceptions(&metadata, EXCEPTIONS_BOOTSTRAP, runtime_ids, bad);
+ let mut checked_runtime_licenses = false;
+ let mut rust_metadata = None;
+
+ for &(workspace, exceptions, permitted_deps) in WORKSPACES {
+ if !root.join(workspace).join("Cargo.lock").exists() {
+ tidy_error!(bad, "the `{workspace}` workspace doesn't have a Cargo.lock");
+ continue;
+ }
+
+ let mut cmd = cargo_metadata::MetadataCommand::new();
+ cmd.cargo_path(cargo)
+ .manifest_path(root.join(workspace).join("Cargo.toml"))
+ .features(cargo_metadata::CargoOpt::AllFeatures)
+ .other_options(vec!["--locked".to_owned()]);
+ let metadata = t!(cmd.exec());
+
+ check_license_exceptions(&metadata, exceptions, bad);
+ if let Some((crates, permitted_deps)) = permitted_deps {
+ check_permitted_dependencies(&metadata, workspace, permitted_deps, crates, bad);
+ }
+
+ if workspace == "." {
+ let runtime_ids = compute_runtime_crates(&metadata);
+ check_runtime_license_exceptions(&metadata, runtime_ids, bad);
+ checked_runtime_licenses = true;
+ rust_metadata = Some(metadata);
+ } else if workspace == "src/tools/cargo" {
+ check_rustfix(
+ rust_metadata
+ .as_ref()
+ .expect("The root workspace should be the first to be checked"),
+ &metadata,
+ bad,
+ );
+ }
+ }
+
+ // Sanity check to ensure we don't accidentally remove the workspace containing the runtime
+ // crates.
+ assert!(checked_runtime_licenses);
}
-/// Check that all licenses are in the valid list in `LICENSES`.
+/// Check that all licenses of runtime dependencies are in the valid list in `LICENSES`.
///
-/// Packages listed in `exceptions` are allowed for tools.
-fn check_license_exceptions(
+/// Unlike for tools we don't allow exceptions to the `LICENSES` list for the runtime with the sole
+/// exception of `fortanix-sgx-abi` which is only used on x86_64-fortanix-unknown-sgx.
+fn check_runtime_license_exceptions(
metadata: &Metadata,
- exceptions: &[(&str, &str)],
runtime_ids: HashSet<&PackageId>,
bad: &mut bool,
) {
+ for pkg in &metadata.packages {
+ if !runtime_ids.contains(&pkg.id) {
+ // Only checking dependencies of runtime libraries here.
+ continue;
+ }
+ if pkg.source.is_none() {
+ // No need to check local packages.
+ continue;
+ }
+ let license = match &pkg.license {
+ Some(license) => license,
+ None => {
+ tidy_error!(bad, "dependency `{}` does not define a license expression", pkg.id);
+ continue;
+ }
+ };
+ if !LICENSES.contains(&license.as_str()) {
+ // This is a specific exception because SGX is considered "third party".
+ // See https://github.com/rust-lang/rust/issues/62620 for more.
+ // In general, these should never be added and this exception
+ // should not be taken as precedent for any new target.
+ if pkg.name == "fortanix-sgx-abi" && pkg.license.as_deref() == Some("MPL-2.0") {
+ continue;
+ }
+ tidy_error!(bad, "invalid license `{}` in `{}`", license, pkg.id);
+ }
+ }
+}
+
+/// Check that all licenses of tool dependencies are in the valid list in `LICENSES`.
+///
+/// Packages listed in `exceptions` are allowed for tools.
+fn check_license_exceptions(metadata: &Metadata, exceptions: &[(&str, &str)], bad: &mut bool) {
// Validate the EXCEPTIONS list hasn't changed.
for (name, license) in exceptions {
// Check that the package actually exists.
@@ -482,24 +613,21 @@ fn check_license_exceptions(
// No need to check local packages.
continue;
}
- if !runtime_ids.contains(&pkg.id) && exception_names.contains(&pkg.name.as_str()) {
+ if exception_names.contains(&pkg.name.as_str()) {
continue;
}
let license = match &pkg.license {
Some(license) => license,
None => {
+ if ICU4X_UNICODE_LICENSE_DEPENDENCIES.contains(&pkg.name.as_str()) {
+ // See the comment on ICU4X_UNICODE_LICENSE_DEPENDENCIES.
+ continue;
+ }
tidy_error!(bad, "dependency `{}` does not define a license expression", pkg.id);
continue;
}
};
if !LICENSES.contains(&license.as_str()) {
- if pkg.name == "fortanix-sgx-abi" {
- // This is a specific exception because SGX is considered
- // "third party". See
- // https://github.com/rust-lang/rust/issues/62620 for more. In
- // general, these should never be added.
- continue;
- }
tidy_error!(bad, "invalid license `{}` in `{}`", license, pkg.id);
}
}
diff --git a/src/tools/tidy/src/extdeps.rs b/src/tools/tidy/src/extdeps.rs
index aad57cacb..ff71ca537 100644
--- a/src/tools/tidy/src/extdeps.rs
+++ b/src/tools/tidy/src/extdeps.rs
@@ -9,25 +9,33 @@ const ALLOWED_SOURCES: &[&str] = &["\"registry+https://github.com/rust-lang/crat
/// Checks for external package sources. `root` is the path to the directory that contains the
/// workspace `Cargo.toml`.
pub fn check(root: &Path, bad: &mut bool) {
- // `Cargo.lock` of rust.
- let path = root.join("Cargo.lock");
+ for &(workspace, _, _) in crate::deps::WORKSPACES {
+ // FIXME check other workspaces too
+ // `Cargo.lock` of rust.
+ let path = root.join(workspace).join("Cargo.lock");
- // Open and read the whole file.
- let cargo_lock = t!(fs::read_to_string(&path));
-
- // Process each line.
- for line in cargo_lock.lines() {
- // Consider only source entries.
- if !line.starts_with("source = ") {
+ if !path.exists() {
+ tidy_error!(bad, "the `{workspace}` workspace doesn't have a Cargo.lock");
continue;
}
- // Extract source value.
- let source = line.split_once('=').unwrap().1.trim();
+ // Open and read the whole file.
+ let cargo_lock = t!(fs::read_to_string(&path));
+
+ // Process each line.
+ for line in cargo_lock.lines() {
+ // Consider only source entries.
+ if !line.starts_with("source = ") {
+ continue;
+ }
+
+ // Extract source value.
+ let source = line.split_once('=').unwrap().1.trim();
- // Ensure source is allowed.
- if !ALLOWED_SOURCES.contains(&&*source) {
- tidy_error!(bad, "invalid source: {}", source);
+ // Ensure source is allowed.
+ if !ALLOWED_SOURCES.contains(&&*source) {
+ tidy_error!(bad, "invalid source: {}", source);
+ }
}
}
}
diff --git a/src/tools/tidy/src/features.rs b/src/tools/tidy/src/features.rs
index d900c04c1..8e791a7dc 100644
--- a/src/tools/tidy/src/features.rs
+++ b/src/tools/tidy/src/features.rs
@@ -30,7 +30,7 @@ const FEATURE_GROUP_END_PREFIX: &str = "// feature-group-end";
#[derive(Debug, PartialEq, Clone)]
pub enum Status {
- Stable,
+ Accepted,
Removed,
Unstable,
}
@@ -38,7 +38,7 @@ pub enum Status {
impl fmt::Display for Status {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let as_str = match *self {
- Status::Stable => "stable",
+ Status::Accepted => "accepted",
Status::Unstable => "unstable",
Status::Removed => "removed",
};
@@ -279,9 +279,9 @@ fn test_filen_gate(filen_underscore: &str, features: &mut Features) -> bool {
pub fn collect_lang_features(base_compiler_path: &Path, bad: &mut bool) -> Features {
let mut features = Features::new();
- collect_lang_features_in(&mut features, base_compiler_path, "active.rs", bad);
collect_lang_features_in(&mut features, base_compiler_path, "accepted.rs", bad);
collect_lang_features_in(&mut features, base_compiler_path, "removed.rs", bad);
+ collect_lang_features_in(&mut features, base_compiler_path, "unstable.rs", bad);
features
}
@@ -336,11 +336,11 @@ fn collect_lang_features_in(features: &mut Features, base: &Path, file: &str, ba
let mut parts = line.split(',');
let level = match parts.next().map(|l| l.trim().trim_start_matches('(')) {
- Some("active") => Status::Unstable,
+ Some("unstable") => Status::Unstable,
Some("incomplete") => Status::Unstable,
Some("internal") => Status::Unstable,
Some("removed") => Status::Removed,
- Some("accepted") => Status::Stable,
+ Some("accepted") => Status::Accepted,
_ => continue,
};
let name = parts.next().unwrap().trim();
@@ -449,7 +449,7 @@ fn get_and_check_lib_features(
Ok((name, f)) => {
let mut check_features = |f: &Feature, list: &Features, display: &str| {
if let Some(ref s) = list.get(name) {
- if f.tracking_issue != s.tracking_issue && f.level != Status::Stable {
+ if f.tracking_issue != s.tracking_issue && f.level != Status::Accepted {
tidy_error!(
bad,
"{}:{}: `issue` \"{}\" mismatches the {} `issue` of \"{}\"",
@@ -566,7 +566,7 @@ fn map_lib_features(
let level = if line.contains("[unstable(") {
Status::Unstable
} else if line.contains("[stable(") {
- Status::Stable
+ Status::Accepted
} else {
continue;
};
@@ -581,7 +581,7 @@ fn map_lib_features(
Some(Err(_err)) => {
err!("malformed stability attribute: can't parse `since` key");
}
- None if level == Status::Stable => {
+ None if level == Status::Accepted => {
err!("malformed stability attribute: missing the `since` key");
}
None => None,
diff --git a/src/tools/tidy/src/lib.rs b/src/tools/tidy/src/lib.rs
index fc69c1432..eb0a2fda2 100644
--- a/src/tools/tidy/src/lib.rs
+++ b/src/tools/tidy/src/lib.rs
@@ -3,8 +3,6 @@
//! This library contains the tidy lints and exposes it
//! to be used by tools.
-use std::fmt::Display;
-
use termcolor::WriteColor;
/// A helper macro to `unwrap` a result except also print out details like:
@@ -31,16 +29,22 @@ macro_rules! t {
macro_rules! tidy_error {
($bad:expr, $($fmt:tt)*) => ({
- $crate::tidy_error($bad, format_args!($($fmt)*)).expect("failed to output error");
+ $crate::tidy_error(&format_args!($($fmt)*).to_string()).expect("failed to output error");
+ *$bad = true;
});
}
-fn tidy_error(bad: &mut bool, args: impl Display) -> std::io::Result<()> {
+macro_rules! tidy_error_ext {
+ ($tidy_error:path, $bad:expr, $($fmt:tt)*) => ({
+ $tidy_error(&format_args!($($fmt)*).to_string()).expect("failed to output error");
+ *$bad = true;
+ });
+}
+
+fn tidy_error(args: &str) -> std::io::Result<()> {
use std::io::Write;
use termcolor::{Color, ColorChoice, ColorSpec, StandardStream};
- *bad = true;
-
let mut stderr = StandardStream::stdout(ColorChoice::Auto);
stderr.set_color(ColorSpec::new().set_fg(Some(Color::Red)))?;
diff --git a/src/tools/tidy/src/mir_opt_tests.rs b/src/tools/tidy/src/mir_opt_tests.rs
index c307bcb93..76feeb343 100644
--- a/src/tools/tidy/src/mir_opt_tests.rs
+++ b/src/tools/tidy/src/mir_opt_tests.rs
@@ -26,7 +26,8 @@ fn check_unused_files(path: &Path, bless: bool, bad: &mut bool) {
for file in rs_files {
for bw in [32, 64] {
for ps in [PanicStrategy::Unwind, PanicStrategy::Abort] {
- for output_file in miropt_test_tools::files_for_miropt_test(&file, bw, ps) {
+ let mir_opt_test = miropt_test_tools::files_for_miropt_test(&file, bw, ps);
+ for output_file in mir_opt_test.files {
output_files.remove(&output_file.expected_file);
}
}
diff --git a/src/tools/tidy/src/style.rs b/src/tools/tidy/src/style.rs
index 11480e2be..cb40c6e3a 100644
--- a/src/tools/tidy/src/style.rs
+++ b/src/tools/tidy/src/style.rs
@@ -427,9 +427,12 @@ pub fn check(path: &Path, bad: &mut bool) {
"copyright notices attributed to the Rust Project Developers are deprecated"
);
}
- if is_unexplained_ignore(&extension, line) {
- err(UNEXPLAINED_IGNORE_DOCTEST_INFO);
+ if !file.components().any(|c| c.as_os_str() == "rustc_baked_icu_data") {
+ if is_unexplained_ignore(&extension, line) {
+ err(UNEXPLAINED_IGNORE_DOCTEST_INFO);
+ }
}
+
if filename.ends_with(".cpp") && line.contains("llvm_unreachable") {
err(LLVM_UNREACHABLE_INFO);
}
diff --git a/src/tools/tidy/src/ui_tests.rs b/src/tools/tidy/src/ui_tests.rs
index 2b828e58d..7e24793ad 100644
--- a/src/tools/tidy/src/ui_tests.rs
+++ b/src/tools/tidy/src/ui_tests.rs
@@ -11,7 +11,7 @@ use std::path::{Path, PathBuf};
const ENTRY_LIMIT: usize = 900;
// FIXME: The following limits should be reduced eventually.
const ISSUES_ENTRY_LIMIT: usize = 1854;
-const ROOT_ENTRY_LIMIT: usize = 865;
+const ROOT_ENTRY_LIMIT: usize = 867;
const EXPECTED_TEST_FILE_EXTENSIONS: &[&str] = &[
"rs", // test source files