about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/bootstrap/builder.rs8
-rw-r--r--src/bootstrap/run.rs22
-rw-r--r--src/bootstrap/test.rs37
-rw-r--r--src/bootstrap/tool.rs1
-rw-r--r--src/tools/replace-version-placeholder/Cargo.toml10
-rw-r--r--src/tools/replace-version-placeholder/src/main.rs30
-rw-r--r--src/tools/tidy/src/features.rs38
-rw-r--r--src/tools/tidy/src/features/version.rs19
-rw-r--r--src/tools/tidy/src/lib.rs74
-rw-r--r--src/tools/tidy/src/walk.rs65
10 files changed, 236 insertions, 68 deletions
diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs
index ba732cd7742..c4710e8faec 100644
--- a/src/bootstrap/builder.rs
+++ b/src/bootstrap/builder.rs
@@ -647,6 +647,7 @@ impl<'a> Builder<'a> {
                 test::CrateRustdocJsonTypes,
                 test::Linkcheck,
                 test::TierCheck,
+                test::ReplacePlaceholderTest,
                 test::Cargotest,
                 test::Cargo,
                 test::Rls,
@@ -746,7 +747,12 @@ impl<'a> Builder<'a> {
                 install::Src,
                 install::Rustc
             ),
-            Kind::Run => describe!(run::ExpandYamlAnchors, run::BuildManifest, run::BumpStage0),
+            Kind::Run => describe!(
+                run::ExpandYamlAnchors,
+                run::BuildManifest,
+                run::BumpStage0,
+                run::ReplaceVersionPlaceholder,
+            ),
             // These commands either don't use paths, or they're special-cased in Build::build()
             Kind::Clean | Kind::Format | Kind::Setup => vec![],
         }
diff --git a/src/bootstrap/run.rs b/src/bootstrap/run.rs
index 25abe7a72fd..511872903d1 100644
--- a/src/bootstrap/run.rs
+++ b/src/bootstrap/run.rs
@@ -103,3 +103,25 @@ impl Step for BumpStage0 {
         builder.run(&mut cmd);
     }
 }
+
+#[derive(Debug, PartialOrd, Ord, Copy, Clone, Hash, PartialEq, Eq)]
+pub struct ReplaceVersionPlaceholder;
+
+impl Step for ReplaceVersionPlaceholder {
+    type Output = ();
+    const ONLY_HOSTS: bool = true;
+
+    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
+        run.path("src/tools/replace-version-placeholder")
+    }
+
+    fn make_run(run: RunConfig<'_>) {
+        run.builder.ensure(ReplaceVersionPlaceholder);
+    }
+
+    fn run(self, builder: &Builder<'_>) -> Self::Output {
+        let mut cmd = builder.tool_cmd(Tool::ReplaceVersionPlaceholder);
+        cmd.arg(&builder.src);
+        builder.run(&mut cmd);
+    }
+}
diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs
index c759d9b88e2..9cbdb3aca32 100644
--- a/src/bootstrap/test.rs
+++ b/src/bootstrap/test.rs
@@ -2528,6 +2528,43 @@ impl Step for TierCheck {
 }
 
 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+pub struct ReplacePlaceholderTest;
+
+impl Step for ReplacePlaceholderTest {
+    type Output = ();
+    const ONLY_HOSTS: bool = true;
+    const DEFAULT: bool = true;
+
+    /// Ensure the version placeholder replacement tool builds
+    fn run(self, builder: &Builder<'_>) {
+        builder.info("build check for version replacement placeholder");
+
+        // Test the version placeholder replacement tool itself.
+        let bootstrap_host = builder.config.build;
+        let compiler = builder.compiler(0, bootstrap_host);
+        let cargo = tool::prepare_tool_cargo(
+            builder,
+            compiler,
+            Mode::ToolBootstrap,
+            bootstrap_host,
+            "test",
+            "src/tools/replace-version-placeholder",
+            SourceType::InTree,
+            &[],
+        );
+        try_run(builder, &mut cargo.into());
+    }
+
+    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
+        run.path("src/tools/replace-version-placeholder")
+    }
+
+    fn make_run(run: RunConfig<'_>) {
+        run.builder.ensure(Self);
+    }
+}
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
 pub struct LintDocs {
     pub compiler: Compiler,
     pub target: TargetSelection,
diff --git a/src/bootstrap/tool.rs b/src/bootstrap/tool.rs
index 570da20e7d6..5bb40014eb9 100644
--- a/src/bootstrap/tool.rs
+++ b/src/bootstrap/tool.rs
@@ -378,6 +378,7 @@ bootstrap_tool!(
     JsonDocCk, "src/tools/jsondocck", "jsondocck";
     HtmlChecker, "src/tools/html-checker", "html-checker";
     BumpStage0, "src/tools/bump-stage0", "bump-stage0";
+    ReplaceVersionPlaceholder, "src/tools/replace-version-placeholder", "replace-version-placeholder";
 );
 
 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, Ord, PartialOrd)]
diff --git a/src/tools/replace-version-placeholder/Cargo.toml b/src/tools/replace-version-placeholder/Cargo.toml
new file mode 100644
index 00000000000..346ce6bd1db
--- /dev/null
+++ b/src/tools/replace-version-placeholder/Cargo.toml
@@ -0,0 +1,10 @@
+[package]
+name = "replace-version-placeholder"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+tidy = { path = "../tidy" }
+walkdir = "2"
diff --git a/src/tools/replace-version-placeholder/src/main.rs b/src/tools/replace-version-placeholder/src/main.rs
new file mode 100644
index 00000000000..146e53f2e9a
--- /dev/null
+++ b/src/tools/replace-version-placeholder/src/main.rs
@@ -0,0 +1,30 @@
+use std::path::PathBuf;
+use tidy::{t, walk};
+
+pub const VERSION_PLACEHOLDER: &str = "CURRENT_RUSTC_VERSION";
+
+fn main() {
+    let root_path: PathBuf = std::env::args_os().nth(1).expect("need path to root of repo").into();
+    let version_path = root_path.join("src").join("version");
+    let version_str = t!(std::fs::read_to_string(&version_path), version_path);
+    let version_str = version_str.trim();
+    walk::walk(
+        &root_path,
+        &mut |path| {
+            walk::filter_dirs(path)
+                // We exempt these as they require the placeholder
+                // for their operation
+                || path.ends_with("compiler/rustc_passes/src/lib_features.rs")
+                || path.ends_with("src/tools/tidy/src/features/version.rs")
+                || path.ends_with("src/tools/replace-version-placeholder")
+        },
+        &mut |entry, contents| {
+            if !contents.contains(VERSION_PLACEHOLDER) {
+                return;
+            }
+            let new_contents = contents.replace(VERSION_PLACEHOLDER, version_str);
+            let path = entry.path();
+            t!(std::fs::write(&path, new_contents), path);
+        },
+    );
+}
diff --git a/src/tools/tidy/src/features.rs b/src/tools/tidy/src/features.rs
index 2f22c081a54..de292d3305d 100644
--- a/src/tools/tidy/src/features.rs
+++ b/src/tools/tidy/src/features.rs
@@ -175,6 +175,36 @@ pub fn check(
         tidy_error!(bad, "Found {} features without a gate test.", gate_untested.len());
     }
 
+    let (version, channel) = get_version_and_channel(src_path);
+
+    let all_features_iter = features
+        .iter()
+        .map(|feat| (feat, "lang"))
+        .chain(lib_features.iter().map(|feat| (feat, "lib")));
+    for ((feature_name, feature), kind) in all_features_iter {
+        let since = if let Some(since) = feature.since { since } else { continue };
+        if since > version && since != Version::CurrentPlaceholder {
+            tidy_error!(
+                bad,
+                "The stabilization version {since} of {kind} feature `{feature_name}` is newer than the current {version}"
+            );
+        }
+        if channel == "nightly" && since == version {
+            tidy_error!(
+                bad,
+                "The stabilization version {since} of {kind} feature `{feature_name}` is written out but should be {}",
+                version::VERSION_PLACEHOLDER
+            );
+        }
+        if channel != "nightly" && since == Version::CurrentPlaceholder {
+            tidy_error!(
+                bad,
+                "The placeholder use of {kind} feature `{feature_name}` is not allowed on the {} channel",
+                version::VERSION_PLACEHOLDER
+            );
+        }
+    }
+
     if *bad {
         return CollectedFeatures { lib: lib_features, lang: features };
     }
@@ -195,6 +225,14 @@ pub fn check(
     CollectedFeatures { lib: lib_features, lang: features }
 }
 
+fn get_version_and_channel(src_path: &Path) -> (Version, String) {
+    let version_str = t!(std::fs::read_to_string(src_path.join("version")));
+    let version_str = version_str.trim();
+    let version = t!(std::str::FromStr::from_str(&version_str).map_err(|e| format!("{e:?}")));
+    let channel_str = t!(std::fs::read_to_string(src_path.join("ci").join("channel")));
+    (version, channel_str.trim().to_owned())
+}
+
 fn format_features<'a>(
     features: &'a Features,
     family: &'a str,
diff --git a/src/tools/tidy/src/features/version.rs b/src/tools/tidy/src/features/version.rs
index 620be2f9852..0830c226caf 100644
--- a/src/tools/tidy/src/features/version.rs
+++ b/src/tools/tidy/src/features/version.rs
@@ -5,14 +5,22 @@ use std::str::FromStr;
 #[cfg(test)]
 mod tests;
 
+pub const VERSION_PLACEHOLDER: &str = "CURRENT_RUSTC_VERSION";
+
 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
-pub struct Version {
-    parts: [u32; 3],
+pub enum Version {
+    Explicit { parts: [u32; 3] },
+    CurrentPlaceholder,
 }
 
 impl fmt::Display for Version {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.pad(&format!("{}.{}.{}", self.parts[0], self.parts[1], self.parts[2]))
+        match self {
+            Version::Explicit { parts } => {
+                f.pad(&format!("{}.{}.{}", parts[0], parts[1], parts[2]))
+            }
+            Version::CurrentPlaceholder => f.pad(&format!("CURRENT")),
+        }
     }
 }
 
@@ -32,6 +40,9 @@ impl FromStr for Version {
     type Err = ParseVersionError;
 
     fn from_str(s: &str) -> Result<Self, Self::Err> {
+        if s == VERSION_PLACEHOLDER {
+            return Ok(Version::CurrentPlaceholder);
+        }
         let mut iter = s.split('.').map(|part| Ok(part.parse()?));
 
         let mut part = || iter.next().unwrap_or(Err(ParseVersionError::WrongNumberOfParts));
@@ -43,6 +54,6 @@ impl FromStr for Version {
             return Err(ParseVersionError::WrongNumberOfParts);
         }
 
-        Ok(Self { parts })
+        Ok(Version::Explicit { parts })
     }
 }
diff --git a/src/tools/tidy/src/lib.rs b/src/tools/tidy/src/lib.rs
index 09848462ae2..12d3bdcd76f 100644
--- a/src/tools/tidy/src/lib.rs
+++ b/src/tools/tidy/src/lib.rs
@@ -3,12 +3,14 @@
 //! This library contains the tidy lints and exposes it
 //! to be used by tools.
 
-use std::fs::File;
-use std::io::Read;
-use walkdir::{DirEntry, WalkDir};
-
-use std::path::Path;
-
+use walk::{filter_dirs, walk, walk_many, walk_no_read};
+
+/// A helper macro to `unwrap` a result except also print out details like:
+///
+/// * The expression that failed
+/// * The error itself
+/// * (optionally) a path connected to the error (e.g. failure to open a file)
+#[macro_export]
 macro_rules! t {
     ($e:expr, $p:expr) => {
         match $e {
@@ -28,7 +30,8 @@ macro_rules! t {
 macro_rules! tidy_error {
     ($bad:expr, $fmt:expr) => ({
         *$bad = true;
-        eprintln!("tidy error: {}", $fmt);
+        eprint!("tidy error: ");
+        eprintln!($fmt);
     });
     ($bad:expr, $fmt:expr, $($arg:tt)*) => ({
         *$bad = true;
@@ -52,59 +55,4 @@ pub mod target_specific_tests;
 pub mod ui_tests;
 pub mod unit_tests;
 pub mod unstable_book;
-
-fn filter_dirs(path: &Path) -> bool {
-    let skip = [
-        "tidy-test-file",
-        "compiler/rustc_codegen_cranelift",
-        "compiler/rustc_codegen_gcc",
-        "src/llvm-project",
-        "library/backtrace",
-        "library/portable-simd",
-        "library/stdarch",
-        "src/tools/cargo",
-        "src/tools/clippy",
-        "src/tools/miri",
-        "src/tools/rls",
-        "src/tools/rust-analyzer",
-        "src/tools/rust-installer",
-        "src/tools/rustfmt",
-        "src/doc/book",
-        // Filter RLS output directories
-        "target/rls",
-    ];
-    skip.iter().any(|p| path.ends_with(p))
-}
-
-fn walk_many(
-    paths: &[&Path],
-    skip: &mut dyn FnMut(&Path) -> bool,
-    f: &mut dyn FnMut(&DirEntry, &str),
-) {
-    for path in paths {
-        walk(path, skip, f);
-    }
-}
-
-fn walk(path: &Path, skip: &mut dyn FnMut(&Path) -> bool, f: &mut dyn FnMut(&DirEntry, &str)) {
-    let mut contents = String::new();
-    walk_no_read(path, skip, &mut |entry| {
-        contents.clear();
-        if t!(File::open(entry.path()), entry.path()).read_to_string(&mut contents).is_err() {
-            contents.clear();
-        }
-        f(&entry, &contents);
-    });
-}
-
-fn walk_no_read(path: &Path, skip: &mut dyn FnMut(&Path) -> bool, f: &mut dyn FnMut(&DirEntry)) {
-    let walker = WalkDir::new(path).into_iter().filter_entry(|e| !skip(e.path()));
-    for entry in walker {
-        if let Ok(entry) = entry {
-            if entry.file_type().is_dir() {
-                continue;
-            }
-            f(&entry);
-        }
-    }
-}
+pub mod walk;
diff --git a/src/tools/tidy/src/walk.rs b/src/tools/tidy/src/walk.rs
new file mode 100644
index 00000000000..6dca55dfa9f
--- /dev/null
+++ b/src/tools/tidy/src/walk.rs
@@ -0,0 +1,65 @@
+use std::fs::File;
+use std::io::Read;
+use walkdir::{DirEntry, WalkDir};
+
+use std::path::Path;
+
+pub fn filter_dirs(path: &Path) -> bool {
+    let skip = [
+        "tidy-test-file",
+        "compiler/rustc_codegen_cranelift",
+        "compiler/rustc_codegen_gcc",
+        "src/llvm-project",
+        "library/backtrace",
+        "library/portable-simd",
+        "library/stdarch",
+        "src/tools/cargo",
+        "src/tools/clippy",
+        "src/tools/miri",
+        "src/tools/rls",
+        "src/tools/rust-analyzer",
+        "src/tools/rust-installer",
+        "src/tools/rustfmt",
+        "src/doc/book",
+        // Filter RLS output directories
+        "target/rls",
+    ];
+    skip.iter().any(|p| path.ends_with(p))
+}
+
+pub fn walk_many(
+    paths: &[&Path],
+    skip: &mut dyn FnMut(&Path) -> bool,
+    f: &mut dyn FnMut(&DirEntry, &str),
+) {
+    for path in paths {
+        walk(path, skip, f);
+    }
+}
+
+pub fn walk(path: &Path, skip: &mut dyn FnMut(&Path) -> bool, f: &mut dyn FnMut(&DirEntry, &str)) {
+    let mut contents = String::new();
+    walk_no_read(path, skip, &mut |entry| {
+        contents.clear();
+        if t!(File::open(entry.path()), entry.path()).read_to_string(&mut contents).is_err() {
+            contents.clear();
+        }
+        f(&entry, &contents);
+    });
+}
+
+pub(crate) fn walk_no_read(
+    path: &Path,
+    skip: &mut dyn FnMut(&Path) -> bool,
+    f: &mut dyn FnMut(&DirEntry),
+) {
+    let walker = WalkDir::new(path).into_iter().filter_entry(|e| !skip(e.path()));
+    for entry in walker {
+        if let Ok(entry) = entry {
+            if entry.file_type().is_dir() {
+                continue;
+            }
+            f(&entry);
+        }
+    }
+}