about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_attr/src/builtin.rs41
-rw-r--r--compiler/rustc_attr/src/lib.rs2
-rw-r--r--compiler/rustc_macros/src/current_version.rs59
-rw-r--r--compiler/rustc_macros/src/lib.rs6
-rw-r--r--compiler/rustc_middle/src/middle/stability.rs27
-rw-r--r--compiler/rustc_session/src/lib.rs3
-rw-r--r--compiler/rustc_session/src/version.rs19
-rw-r--r--src/librustdoc/html/render/mod.rs5
-rw-r--r--src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs23
9 files changed, 124 insertions, 61 deletions
diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs
index 363ba0d4a81..ff23e3da438 100644
--- a/compiler/rustc_attr/src/builtin.rs
+++ b/compiler/rustc_attr/src/builtin.rs
@@ -10,10 +10,9 @@ use rustc_session::config::ExpectedValues;
 use rustc_session::lint::builtin::UNEXPECTED_CFGS;
 use rustc_session::lint::BuiltinLintDiagnostics;
 use rustc_session::parse::{feature_err, ParseSess};
-use rustc_session::Session;
+use rustc_session::{RustcVersion, Session};
 use rustc_span::hygiene::Transparency;
 use rustc_span::{symbol::sym, symbol::Symbol, Span};
-use std::fmt::{self, Display};
 use std::num::NonZeroU32;
 
 use crate::session_diagnostics::{self, IncorrectReprFormatGenericCause};
@@ -24,8 +23,6 @@ use crate::session_diagnostics::{self, IncorrectReprFormatGenericCause};
 /// For more, see [this pull request](https://github.com/rust-lang/rust/pull/100591).
 pub const VERSION_PLACEHOLDER: &str = "CURRENT_RUSTC_VERSION";
 
-pub const CURRENT_RUSTC_VERSION: &str = env!("CFG_RELEASE");
-
 pub fn is_builtin_attr(attr: &Attribute) -> bool {
     attr.is_doc_comment() || attr.ident().is_some_and(|ident| is_builtin_attr_name(ident.name))
 }
@@ -153,7 +150,7 @@ pub enum StabilityLevel {
 #[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, Hash)]
 #[derive(HashStable_Generic)]
 pub enum Since {
-    Version(Version),
+    Version(RustcVersion),
     /// Stabilized in the upcoming version, whatever number that is.
     Current,
     /// Failed to parse a stabilization version.
@@ -382,7 +379,7 @@ fn parse_stability(sess: &Session, attr: &Attribute) -> Option<(Symbol, Stabilit
     let since = if let Some(since) = since {
         if since.as_str() == VERSION_PLACEHOLDER {
             Since::Current
-        } else if let Some(version) = parse_version(since.as_str(), false) {
+        } else if let Some(version) = parse_version(since) {
             Since::Version(version)
         } else {
             sess.emit_err(session_diagnostics::InvalidSince { span: attr.span });
@@ -567,31 +564,20 @@ fn gate_cfg(gated_cfg: &GatedCfg, cfg_span: Span, sess: &ParseSess, features: &F
     }
 }
 
-#[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
-#[derive(HashStable_Generic)]
-pub struct Version {
-    pub major: u16,
-    pub minor: u16,
-    pub patch: u16,
-}
-
-fn parse_version(s: &str, allow_appendix: bool) -> Option<Version> {
-    let mut components = s.split('-');
+/// Parse a rustc version number written inside string literal in an attribute,
+/// like appears in `since = "1.0.0"`. Suffixes like "-dev" and "-nightly" are
+/// not accepted in this position, unlike when parsing CFG_RELEASE.
+fn parse_version(s: Symbol) -> Option<RustcVersion> {
+    let mut components = s.as_str().split('-');
     let d = components.next()?;
-    if !allow_appendix && components.next().is_some() {
+    if components.next().is_some() {
         return None;
     }
     let mut digits = d.splitn(3, '.');
     let major = digits.next()?.parse().ok()?;
     let minor = digits.next()?.parse().ok()?;
     let patch = digits.next().unwrap_or("0").parse().ok()?;
-    Some(Version { major, minor, patch })
-}
-
-impl Display for Version {
-    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(formatter, "{}.{}.{}", self.major, self.minor, self.patch)
-    }
+    Some(RustcVersion { major, minor, patch })
 }
 
 /// Evaluate a cfg-like condition (with `any` and `all`), using `eval` to
@@ -623,17 +609,16 @@ pub fn eval_condition(
                     return false;
                 }
             };
-            let Some(min_version) = parse_version(min_version.as_str(), false) else {
+            let Some(min_version) = parse_version(*min_version) else {
                 sess.emit_warning(session_diagnostics::UnknownVersionLiteral { span: *span });
                 return false;
             };
-            let rustc_version = parse_version(CURRENT_RUSTC_VERSION, true).unwrap();
 
             // See https://github.com/rust-lang/rust/issues/64796#issuecomment-640851454 for details
             if sess.assume_incomplete_release {
-                rustc_version > min_version
+                RustcVersion::CURRENT > min_version
             } else {
-                rustc_version >= min_version
+                RustcVersion::CURRENT >= min_version
             }
         }
         ast::MetaItemKind::List(mis) => {
diff --git a/compiler/rustc_attr/src/lib.rs b/compiler/rustc_attr/src/lib.rs
index 53e3eaaab37..868c0412255 100644
--- a/compiler/rustc_attr/src/lib.rs
+++ b/compiler/rustc_attr/src/lib.rs
@@ -27,6 +27,6 @@ pub use StabilityLevel::*;
 
 pub use rustc_ast::attr::*;
 
-pub(crate) use rustc_ast::HashStableContext;
+pub(crate) use rustc_session::HashStableContext;
 
 fluent_messages! { "../messages.ftl" }
diff --git a/compiler/rustc_macros/src/current_version.rs b/compiler/rustc_macros/src/current_version.rs
new file mode 100644
index 00000000000..5e3b91c17bf
--- /dev/null
+++ b/compiler/rustc_macros/src/current_version.rs
@@ -0,0 +1,59 @@
+use proc_macro::TokenStream;
+use proc_macro2::Span;
+use quote::quote;
+use syn::parse::{Parse, ParseStream};
+use syn::{parenthesized, parse_macro_input, LitStr, Token};
+
+pub struct Input {
+    variable: LitStr,
+}
+
+mod kw {
+    syn::custom_keyword!(env);
+}
+
+impl Parse for Input {
+    // Input syntax is `env!("CFG_RELEASE")` to facilitate grepping.
+    fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
+        let paren;
+        input.parse::<kw::env>()?;
+        input.parse::<Token![!]>()?;
+        parenthesized!(paren in input);
+        let variable: LitStr = paren.parse()?;
+        Ok(Input { variable })
+    }
+}
+
+pub(crate) fn current_version(input: TokenStream) -> TokenStream {
+    let input = parse_macro_input!(input as Input);
+
+    TokenStream::from(match RustcVersion::parse_env_var(&input.variable) {
+        Ok(RustcVersion { major, minor, patch }) => quote!(
+            Self { major: #major, minor: #minor, patch: #patch }
+        ),
+        Err(err) => syn::Error::new(Span::call_site(), err).into_compile_error(),
+    })
+}
+
+struct RustcVersion {
+    major: u16,
+    minor: u16,
+    patch: u16,
+}
+
+impl RustcVersion {
+    fn parse_env_var(env_var: &LitStr) -> Result<Self, Box<dyn std::error::Error>> {
+        let value = proc_macro::tracked_env::var(env_var.value())?;
+        Self::parse_str(&value)
+            .ok_or_else(|| format!("failed to parse rustc version: {:?}", value).into())
+    }
+
+    fn parse_str(value: &str) -> Option<Self> {
+        // Ignore any suffixes such as "-dev" or "-nightly".
+        let mut components = value.split('-').next().unwrap().splitn(3, '.');
+        let major = components.next()?.parse().ok()?;
+        let minor = components.next()?.parse().ok()?;
+        let patch = components.next().unwrap_or("0").parse().ok()?;
+        Some(RustcVersion { major, minor, patch })
+    }
+}
diff --git a/compiler/rustc_macros/src/lib.rs b/compiler/rustc_macros/src/lib.rs
index 776ba8f9ca1..193dbd75fbd 100644
--- a/compiler/rustc_macros/src/lib.rs
+++ b/compiler/rustc_macros/src/lib.rs
@@ -15,6 +15,7 @@ use synstructure::decl_derive;
 
 use proc_macro::TokenStream;
 
+mod current_version;
 mod diagnostics;
 mod hash_stable;
 mod lift;
@@ -26,6 +27,11 @@ mod type_foldable;
 mod type_visitable;
 
 #[proc_macro]
+pub fn current_rustc_version(input: TokenStream) -> TokenStream {
+    current_version::current_version(input)
+}
+
+#[proc_macro]
 pub fn rustc_queries(input: TokenStream) -> TokenStream {
     query::rustc_queries(input)
 }
diff --git a/compiler/rustc_middle/src/middle/stability.rs b/compiler/rustc_middle/src/middle/stability.rs
index 84893b8e627..20547696d5a 100644
--- a/compiler/rustc_middle/src/middle/stability.rs
+++ b/compiler/rustc_middle/src/middle/stability.rs
@@ -16,7 +16,7 @@ use rustc_middle::ty::print::with_no_trimmed_paths;
 use rustc_session::lint::builtin::{DEPRECATED, DEPRECATED_IN_FUTURE, SOFT_UNSTABLE};
 use rustc_session::lint::{BuiltinLintDiagnostics, Level, Lint, LintBuffer};
 use rustc_session::parse::feature_err_issue;
-use rustc_session::Session;
+use rustc_session::{RustcVersion, Session};
 use rustc_span::symbol::{sym, Symbol};
 use rustc_span::Span;
 use std::num::NonZeroU32;
@@ -129,11 +129,6 @@ pub fn deprecation_in_effect(depr: &Deprecation) -> bool {
     let is_since_rustc_version = depr.is_since_rustc_version;
     let since = depr.since.as_ref().map(Symbol::as_str);
 
-    fn parse_version(ver: &str) -> Vec<u32> {
-        // We ignore non-integer components of the version (e.g., "nightly").
-        ver.split(|c| c == '.' || c == '-').flat_map(|s| s.parse()).collect()
-    }
-
     if !is_since_rustc_version {
         // The `since` field doesn't have semantic purpose without `#![staged_api]`.
         return true;
@@ -144,16 +139,18 @@ pub fn deprecation_in_effect(depr: &Deprecation) -> bool {
             return false;
         }
 
-        if let Some(rustc) = option_env!("CFG_RELEASE") {
-            let since: Vec<u32> = parse_version(&since);
-            let rustc: Vec<u32> = parse_version(rustc);
-            // We simply treat invalid `since` attributes as relating to a previous
-            // Rust version, thus always displaying the warning.
-            if since.len() != 3 {
-                return true;
-            }
-            return since <= rustc;
+        // We ignore non-integer components of the version (e.g., "nightly").
+        let since: Vec<u16> =
+            since.split(|c| c == '.' || c == '-').flat_map(|s| s.parse()).collect();
+
+        // We simply treat invalid `since` attributes as relating to a previous
+        // Rust version, thus always displaying the warning.
+        if since.len() != 3 {
+            return true;
         }
+
+        let rustc = RustcVersion::CURRENT;
+        return since.as_slice() <= &[rustc.major, rustc.minor, rustc.patch];
     };
 
     // Assume deprecation is in effect if "since" field is missing
diff --git a/compiler/rustc_session/src/lib.rs b/compiler/rustc_session/src/lib.rs
index 7da0bcf01bf..17ac3e991c5 100644
--- a/compiler/rustc_session/src/lib.rs
+++ b/compiler/rustc_session/src/lib.rs
@@ -43,6 +43,9 @@ pub mod output;
 
 pub use getopts;
 
+mod version;
+pub use version::RustcVersion;
+
 fluent_messages! { "../messages.ftl" }
 
 /// Requirements for a `StableHashingContext` to be used in this crate.
diff --git a/compiler/rustc_session/src/version.rs b/compiler/rustc_session/src/version.rs
new file mode 100644
index 00000000000..1ad8620bfba
--- /dev/null
+++ b/compiler/rustc_session/src/version.rs
@@ -0,0 +1,19 @@
+use std::fmt::{self, Display};
+
+#[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[derive(HashStable_Generic)]
+pub struct RustcVersion {
+    pub major: u16,
+    pub minor: u16,
+    pub patch: u16,
+}
+
+impl RustcVersion {
+    pub const CURRENT: Self = current_rustc_version!(env!("CFG_RELEASE"));
+}
+
+impl Display for RustcVersion {
+    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(formatter, "{}.{}.{}", self.major, self.minor, self.patch)
+    }
+}
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index d9086433608..0b6079b2933 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -48,13 +48,14 @@ use std::str;
 use std::string::ToString;
 
 use askama::Template;
-use rustc_attr::{ConstStability, Deprecation, Since, StabilityLevel, CURRENT_RUSTC_VERSION};
+use rustc_attr::{ConstStability, Deprecation, Since, StabilityLevel};
 use rustc_data_structures::captures::Captures;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_hir::def_id::{DefId, DefIdSet};
 use rustc_hir::Mutability;
 use rustc_middle::middle::stability;
 use rustc_middle::ty::{self, TyCtxt};
+use rustc_session::RustcVersion;
 use rustc_span::{
     symbol::{sym, Symbol},
     BytePos, FileName, RealFileName,
@@ -979,7 +980,7 @@ fn render_stability_since_raw_with_extra(
 fn since_to_string(since: &Since) -> Option<String> {
     match since {
         Since::Version(since) => Some(since.to_string()),
-        Since::Current => Some(CURRENT_RUSTC_VERSION.to_owned()),
+        Since::Current => Some(RustcVersion::CURRENT.to_string()),
         Since::Err => None,
     }
 }
diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs
index 1e465ac91b7..31f7b87de63 100644
--- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs
+++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs
@@ -5,7 +5,7 @@
 
 use crate::msrvs::Msrv;
 use hir::LangItem;
-use rustc_attr::{Since, CURRENT_RUSTC_VERSION};
+use rustc_attr::Since;
 use rustc_const_eval::transform::check_consts::ConstCx;
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
@@ -372,23 +372,16 @@ fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: &Msrv) -> bool {
                 // as a part of an unimplemented MSRV check https://github.com/rust-lang/rust/issues/65262.
 
                 let const_stab_rust_version = match since {
-                    Since::Version(version) => RustcVersion::new(
-                        u32::from(version.major),
-                        u32::from(version.minor),
-                        u32::from(version.patch),
-                    ),
-                    Since::Current => {
-                        // HACK(nilstrieb): CURRENT_RUSTC_VERSION can return versions like 1.66.0-dev.
-                        // `rustc-semver` doesn't accept the `-dev` version number so we have to strip it off.
-                        let short_version = CURRENT_RUSTC_VERSION.split('-').next().unwrap();
-                        RustcVersion::parse(short_version).unwrap_or_else(|err| {
-                            panic!("`rustc_attr::StabilityLevel::Stable::since` is ill-formatted: `{CURRENT_RUSTC_VERSION}`, {err:?}")
-                        })
-                    },
+                    Since::Version(version) => version,
+                    Since::Current => rustc_session::RustcVersion::CURRENT,
                     Since::Err => return false,
                 };
 
-                msrv.meets(const_stab_rust_version)
+                msrv.meets(RustcVersion::new(
+                    u32::from(const_stab_rust_version.major),
+                    u32::from(const_stab_rust_version.minor),
+                    u32::from(const_stab_rust_version.patch),
+                ))
             } else {
                 // Unstable const fn with the feature enabled.
                 msrv.current().is_none()