From f2ae7b55ae3283d37fa7cca6b4740981af51b845 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 23 Oct 2023 21:59:22 -0700 Subject: [PATCH 1/7] Store 'since' attribute as parsed Version --- compiler/rustc_attr/src/builtin.rs | 28 ++++++++---- compiler/rustc_passes/messages.ftl | 5 --- compiler/rustc_passes/src/errors.rs | 10 ----- compiler/rustc_passes/src/stability.rs | 60 +++++++++++++------------- 4 files changed, 50 insertions(+), 53 deletions(-) diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs index 44ba495721d..d01eae383a4 100644 --- a/compiler/rustc_attr/src/builtin.rs +++ b/compiler/rustc_attr/src/builtin.rs @@ -144,13 +144,22 @@ pub enum StabilityLevel { /// `#[stable]` Stable { /// Rust release which stabilized this feature. - since: Symbol, + since: Since, /// Is this item allowed to be referred to on stable, despite being contained in unstable /// modules? allowed_through_unstable_modules: bool, }, } +/// Rust release in which a feature is stabilized. +#[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, Hash)] +#[derive(HashStable_Generic)] +pub enum Since { + Version(Version), + /// Stabilized in the upcoming version, whatever number that is. + Current, +} + impl StabilityLevel { pub fn is_unstable(&self) -> bool { matches!(self, StabilityLevel::Unstable { .. }) @@ -372,9 +381,9 @@ fn parse_stability(sess: &Session, attr: &Attribute) -> Option<(Symbol, Stabilit let since = if let Some(since) = since { if since.as_str() == VERSION_PLACEHOLDER { - Ok(rust_version_symbol()) - } else if parse_version(since.as_str(), false).is_some() { - Ok(since) + Ok(Since::Current) + } else if let Some(version) = parse_version(since.as_str(), false) { + Ok(Since::Version(version)) } else { Err(sess.emit_err(session_diagnostics::InvalidSince { span: attr.span })) } @@ -556,11 +565,12 @@ fn gate_cfg(gated_cfg: &GatedCfg, cfg_span: Span, sess: &ParseSess, features: &F } } -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] -struct Version { - major: u16, - minor: u16, - patch: u16, +#[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 { diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index 25ef5245cf1..84d3b84e13f 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -402,11 +402,6 @@ passes_invalid_macro_export_arguments = `{$name}` isn't a valid `#[macro_export] passes_invalid_macro_export_arguments_too_many_items = `#[macro_export]` can only take 1 or 0 arguments -passes_invalid_stability = - invalid stability version found - .label = invalid stability version - .item = the stability attribute annotates this item - passes_lang_item_fn_with_target_feature = `{$name}` language item function is not allowed to have `#[target_feature]` .label = `{$name}` language item function is not allowed to have `#[target_feature]` diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index 6f87b56c636..6e840f24c69 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -1504,16 +1504,6 @@ pub struct UselessStability { pub item_sp: Span, } -#[derive(Diagnostic)] -#[diag(passes_invalid_stability)] -pub struct InvalidStability { - #[primary_span] - #[label] - pub span: Span, - #[label(passes_item)] - pub item_sp: Span, -} - #[derive(Diagnostic)] #[diag(passes_cannot_stabilize_deprecated)] pub struct CannotStabilizeDeprecated { diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index bb23dc257d7..d19df83162c 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -3,7 +3,7 @@ use crate::errors; use rustc_attr::{ - self as attr, rust_version_symbol, ConstStability, Stability, StabilityLevel, Unstable, + self as attr, rust_version_symbol, ConstStability, Since, Stability, StabilityLevel, Unstable, UnstableReason, VERSION_PLACEHOLDER, }; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; @@ -226,37 +226,39 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { if let (&Some(dep_since), &attr::Stable { since: stab_since, .. }) = (&depr.as_ref().and_then(|(d, _)| d.since), &stab.level) { - // Explicit version of iter::order::lt to handle parse errors properly - for (dep_v, stab_v) in - iter::zip(dep_since.as_str().split('.'), stab_since.as_str().split('.')) - { - match stab_v.parse::() { - Err(_) => { - self.tcx.sess.emit_err(errors::InvalidStability { span, item_sp }); - break; - } - Ok(stab_vp) => match dep_v.parse::() { - Ok(dep_vp) => match dep_vp.cmp(&stab_vp) { - Ordering::Less => { - self.tcx.sess.emit_err(errors::CannotStabilizeDeprecated { - span, - item_sp, - }); + match stab_since { + Since::Current => { + self.tcx.sess.emit_err(errors::CannotStabilizeDeprecated { span, item_sp }); + } + Since::Version(stab_since) => { + // Explicit version of iter::order::lt to handle parse errors properly + for (dep_v, stab_v) in iter::zip( + dep_since.as_str().split('.'), + [stab_since.major, stab_since.minor, stab_since.patch], + ) { + match dep_v.parse::() { + Ok(dep_vp) => match dep_vp.cmp(&u64::from(stab_v)) { + Ordering::Less => { + self.tcx.sess.emit_err(errors::CannotStabilizeDeprecated { + span, + item_sp, + }); + break; + } + Ordering::Equal => continue, + Ordering::Greater => break, + }, + Err(_) => { + if dep_v != "TBD" { + self.tcx.sess.emit_err(errors::InvalidDeprecationVersion { + span, + item_sp, + }); + } break; } - Ordering::Equal => continue, - Ordering::Greater => break, - }, - Err(_) => { - if dep_v != "TBD" { - self.tcx.sess.emit_err(errors::InvalidDeprecationVersion { - span, - item_sp, - }); - } - break; } - }, + } } } } From ddcb1833ff0d0c4d6bf26163f64e4ad1617e65ff Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 23 Oct 2023 23:06:52 -0700 Subject: [PATCH 2/7] Keep track of #[stable] attribute even if version cannot be parsed --- compiler/rustc_attr/src/builtin.rs | 18 +++++++++++------- compiler/rustc_passes/src/stability.rs | 4 ++++ 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs index d01eae383a4..7081e285d68 100644 --- a/compiler/rustc_attr/src/builtin.rs +++ b/compiler/rustc_attr/src/builtin.rs @@ -158,6 +158,8 @@ pub enum Since { Version(Version), /// Stabilized in the upcoming version, whatever number that is. Current, + /// Failed to parse a stabilization version. + Err, } impl StabilityLevel { @@ -381,22 +383,24 @@ fn parse_stability(sess: &Session, attr: &Attribute) -> Option<(Symbol, Stabilit let since = if let Some(since) = since { if since.as_str() == VERSION_PLACEHOLDER { - Ok(Since::Current) + Since::Current } else if let Some(version) = parse_version(since.as_str(), false) { - Ok(Since::Version(version)) + Since::Version(version) } else { - Err(sess.emit_err(session_diagnostics::InvalidSince { span: attr.span })) + sess.emit_err(session_diagnostics::InvalidSince { span: attr.span }); + Since::Err } } else { - Err(sess.emit_err(session_diagnostics::MissingSince { span: attr.span })) + sess.emit_err(session_diagnostics::MissingSince { span: attr.span }); + Since::Err }; - match (feature, since) { - (Ok(feature), Ok(since)) => { + match feature { + Ok(feature) => { let level = StabilityLevel::Stable { since, allowed_through_unstable_modules: false }; Some((feature, level)) } - (Err(ErrorGuaranteed { .. }), _) | (_, Err(ErrorGuaranteed { .. })) => None, + Err(ErrorGuaranteed { .. }) => None, } } diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index d19df83162c..41a240fa880 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -260,6 +260,10 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { } } } + Since::Err => { + // An error already reported. Assume the unparseable stabilization + // version is older than the deprecation version. + } } } From 7b0e315ae6784c344d5c19a5bb83d96bc20f518e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 23 Oct 2023 23:11:53 -0700 Subject: [PATCH 3/7] Update stability attribute sanity UI test to delete superfluous errors --- .../stability-attribute-sanity.rs | 3 +-- .../stability-attribute-sanity.stderr | 22 +++++-------------- 2 files changed, 6 insertions(+), 19 deletions(-) diff --git a/tests/ui/stability-attribute/stability-attribute-sanity.rs b/tests/ui/stability-attribute/stability-attribute-sanity.rs index 7b3a7b537c1..8258b6f5ae0 100644 --- a/tests/ui/stability-attribute/stability-attribute-sanity.rs +++ b/tests/ui/stability-attribute/stability-attribute-sanity.rs @@ -60,10 +60,9 @@ fn multiple3() { } #[stable(feature = "e", since = "b")] //~ ERROR 'since' must be a Rust version number, such as "1.31.0" #[deprecated(since = "b", note = "text")] #[deprecated(since = "b", note = "text")] //~ ERROR multiple `deprecated` attributes -//~^ ERROR deprecated attribute must be paired with either stable or unstable attribute #[rustc_const_unstable(feature = "c", issue = "none")] #[rustc_const_unstable(feature = "d", issue = "none")] //~ ERROR multiple stability levels -pub const fn multiple4() { } //~ ERROR function has missing stability attribute +pub const fn multiple4() { } #[stable(feature = "a", since = "1.0.0")] //~ ERROR invalid deprecation version found //~^ ERROR feature `a` is declared stable since 1.0.0 diff --git a/tests/ui/stability-attribute/stability-attribute-sanity.stderr b/tests/ui/stability-attribute/stability-attribute-sanity.stderr index f9610c90f76..955230742bd 100644 --- a/tests/ui/stability-attribute/stability-attribute-sanity.stderr +++ b/tests/ui/stability-attribute/stability-attribute-sanity.stderr @@ -101,19 +101,13 @@ LL | #[stable(feature = "e", since = "b")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0544]: multiple stability levels - --> $DIR/stability-attribute-sanity.rs:65:1 + --> $DIR/stability-attribute-sanity.rs:64:1 | LL | #[rustc_const_unstable(feature = "d", issue = "none")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0549]: deprecated attribute must be paired with either stable or unstable attribute - --> $DIR/stability-attribute-sanity.rs:62:1 - | -LL | #[deprecated(since = "b", note = "text")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - error: invalid deprecation version found - --> $DIR/stability-attribute-sanity.rs:68:1 + --> $DIR/stability-attribute-sanity.rs:67:1 | LL | #[stable(feature = "a", since = "1.0.0")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid deprecation version @@ -122,24 +116,18 @@ LL | fn invalid_deprecation_version() {} | ----------------------------------- the stability attribute annotates this item error[E0549]: deprecated attribute must be paired with either stable or unstable attribute - --> $DIR/stability-attribute-sanity.rs:73:1 + --> $DIR/stability-attribute-sanity.rs:72:1 | LL | #[deprecated(since = "a", note = "text")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: function has missing stability attribute - --> $DIR/stability-attribute-sanity.rs:66:1 - | -LL | pub const fn multiple4() { } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - error[E0711]: feature `a` is declared stable since 1.0.0, but was previously declared stable since 4.4.4 - --> $DIR/stability-attribute-sanity.rs:68:1 + --> $DIR/stability-attribute-sanity.rs:67:1 | LL | #[stable(feature = "a", since = "1.0.0")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 22 previous errors +error: aborting due to 20 previous errors Some errors have detailed explanations: E0539, E0541, E0542, E0543, E0544, E0546, E0547, E0549, E0711. For more information about an error, try `rustc --explain E0539`. From 6933a671d32f6f8077404e6d558c7ba60bdbbfb1 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 24 Oct 2023 16:35:43 -0700 Subject: [PATCH 4/7] Handle structured stable attribute 'since' version in rustdoc --- compiler/rustc_attr/src/builtin.rs | 7 +++++ src/librustdoc/clean/types.rs | 6 ++-- src/librustdoc/html/render/mod.rs | 31 +++++++++++++------ tests/rustdoc/html-no-source.rs | 12 +++---- tests/rustdoc/source-version-separator.rs | 10 +++--- .../version-separator-without-source.rs | 12 +++---- 6 files changed, 49 insertions(+), 29 deletions(-) diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs index 7081e285d68..a31b683b1af 100644 --- a/compiler/rustc_attr/src/builtin.rs +++ b/compiler/rustc_attr/src/builtin.rs @@ -13,6 +13,7 @@ use rustc_session::parse::{feature_err, ParseSess}; use rustc_session::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}; @@ -590,6 +591,12 @@ fn parse_version(s: &str, allow_appendix: bool) -> Option { 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) + } +} + /// Evaluate a cfg-like condition (with `any` and `all`), using `eval` to /// evaluate individual items. pub fn eval_condition( diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 6a7410144fd..449aac4cfc8 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -12,7 +12,7 @@ use thin_vec::ThinVec; use rustc_ast as ast; use rustc_ast_pretty::pprust; -use rustc_attr::{ConstStability, Deprecation, Stability, StabilityLevel}; +use rustc_attr::{ConstStability, Deprecation, Since, Stability, StabilityLevel}; use rustc_const_eval::const_eval::is_unstable_const_fn; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir as hir; @@ -585,14 +585,14 @@ impl Item { }) } - pub(crate) fn stable_since(&self, tcx: TyCtxt<'_>) -> Option { + pub(crate) fn stable_since(&self, tcx: TyCtxt<'_>) -> Option { match self.stability(tcx)?.level { StabilityLevel::Stable { since, .. } => Some(since), StabilityLevel::Unstable { .. } => None, } } - pub(crate) fn const_stable_since(&self, tcx: TyCtxt<'_>) -> Option { + pub(crate) fn const_stable_since(&self, tcx: TyCtxt<'_>) -> Option { match self.const_stability(tcx)?.level { StabilityLevel::Stable { since, .. } => Some(since), StabilityLevel::Unstable { .. } => None, diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 89e29d8b59b..1aad014c571 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -48,7 +48,7 @@ use std::str; use std::string::ToString; use askama::Template; -use rustc_attr::{ConstStability, Deprecation, StabilityLevel}; +use rustc_attr::{rust_version_symbol, ConstStability, Deprecation, Since, StabilityLevel}; use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::def_id::{DefId, DefIdSet}; @@ -911,13 +911,17 @@ fn assoc_method( /// consequence of the above rules. fn render_stability_since_raw_with_extra( w: &mut Buffer, - ver: Option, + ver: Option, const_stability: Option, - containing_ver: Option, - containing_const_ver: Option, + containing_ver: Option, + containing_const_ver: Option, extra_class: &str, ) -> bool { - let stable_version = ver.filter(|inner| !inner.is_empty() && Some(*inner) != containing_ver); + let stable_version = if ver != containing_ver && let Some(ver) = &ver { + since_to_string(ver) + } else { + None + }; let mut title = String::new(); let mut stability = String::new(); @@ -931,7 +935,8 @@ fn render_stability_since_raw_with_extra( Some(ConstStability { level: StabilityLevel::Stable { since, .. }, .. }) if Some(since) != containing_const_ver => { - Some((format!("const since {since}"), format!("const: {since}"))) + since_to_string(&since) + .map(|since| (format!("const since {since}"), format!("const: {since}"))) } Some(ConstStability { level: StabilityLevel::Unstable { issue, .. }, feature, .. }) => { let unstable = if let Some(n) = issue { @@ -971,13 +976,21 @@ fn render_stability_since_raw_with_extra( !stability.is_empty() } +fn since_to_string(since: &Since) -> Option { + match since { + Since::Version(since) => Some(since.to_string()), + Since::Current => Some(rust_version_symbol().to_string()), + Since::Err => None, + } +} + #[inline] fn render_stability_since_raw( w: &mut Buffer, - ver: Option, + ver: Option, const_stability: Option, - containing_ver: Option, - containing_const_ver: Option, + containing_ver: Option, + containing_const_ver: Option, ) -> bool { render_stability_since_raw_with_extra( w, diff --git a/tests/rustdoc/html-no-source.rs b/tests/rustdoc/html-no-source.rs index 25615a73c3f..b91aa41207a 100644 --- a/tests/rustdoc/html-no-source.rs +++ b/tests/rustdoc/html-no-source.rs @@ -11,20 +11,20 @@ // @files 'src/foo' '[]' // @has foo/fn.foo.html -// @has - '//div[@class="main-heading"]/*[@class="out-of-band"]' '1.0 · ' -// @!has - '//div[@class="main-heading"]/*[@class="out-of-band"]' '1.0 · source · ' +// @has - '//div[@class="main-heading"]/*[@class="out-of-band"]' '1.0.0 · ' +// @!has - '//div[@class="main-heading"]/*[@class="out-of-band"]' '1.0.0 · source · ' #[stable(feature = "bar", since = "1.0")] pub fn foo() {} // @has foo/struct.Bar.html -// @has - '//div[@class="main-heading"]/*[@class="out-of-band"]' '1.0 · ' -// @!has - '//div[@class="main-heading"]/*[@class="out-of-band"]' '1.0 · source · ' +// @has - '//div[@class="main-heading"]/*[@class="out-of-band"]' '1.0.0 · ' +// @!has - '//div[@class="main-heading"]/*[@class="out-of-band"]' '1.0.0 · source · ' #[stable(feature = "bar", since = "1.0")] pub struct Bar; impl Bar { - // @has - '//*[@id="method.bar"]/*[@class="since rightside"]' '2.0' - // @!has - '//*[@id="method.bar"]/*[@class="rightside"]' '2.0 ·' + // @has - '//*[@id="method.bar"]/*[@class="since rightside"]' '2.0.0' + // @!has - '//*[@id="method.bar"]/*[@class="rightside"]' '2.0.0 ·' #[stable(feature = "foobar", since = "2.0")] pub fn bar() {} } diff --git a/tests/rustdoc/source-version-separator.rs b/tests/rustdoc/source-version-separator.rs index 14580373b3b..7256f731573 100644 --- a/tests/rustdoc/source-version-separator.rs +++ b/tests/rustdoc/source-version-separator.rs @@ -3,23 +3,23 @@ #![feature(staged_api)] // @has foo/trait.Bar.html -// @has - '//div[@class="main-heading"]/*[@class="out-of-band"]' '1.0 · source · ' +// @has - '//div[@class="main-heading"]/*[@class="out-of-band"]' '1.0.0 · source · ' #[stable(feature = "bar", since = "1.0")] pub trait Bar { - // @has - '//*[@id="tymethod.foo"]/*[@class="rightside"]' '3.0 · source' + // @has - '//*[@id="tymethod.foo"]/*[@class="rightside"]' '3.0.0 · source' #[stable(feature = "foobar", since = "3.0")] fn foo(); } -// @has - '//div[@id="implementors-list"]//*[@class="rightside"]' '4.0 · source' +// @has - '//div[@id="implementors-list"]//*[@class="rightside"]' '4.0.0 · source' // @has foo/struct.Foo.html -// @has - '//div[@class="main-heading"]/*[@class="out-of-band"]' '1.0 · source · ' +// @has - '//div[@class="main-heading"]/*[@class="out-of-band"]' '1.0.0 · source · ' #[stable(feature = "baz", since = "1.0")] pub struct Foo; impl Foo { - // @has - '//*[@id="method.foofoo"]/*[@class="rightside"]' '3.0 · source' + // @has - '//*[@id="method.foofoo"]/*[@class="rightside"]' '3.0.0 · source' #[stable(feature = "foobar", since = "3.0")] pub fn foofoo() {} } diff --git a/tests/rustdoc/version-separator-without-source.rs b/tests/rustdoc/version-separator-without-source.rs index 04ea46a7f3a..4a855b7bb29 100644 --- a/tests/rustdoc/version-separator-without-source.rs +++ b/tests/rustdoc/version-separator-without-source.rs @@ -4,20 +4,20 @@ #![crate_name = "foo"] // @has foo/fn.foo.html -// @has - '//div[@class="main-heading"]/*[@class="out-of-band"]' '1.0 · ' -// @!has - '//div[@class="main-heading"]/*[@class="out-of-band"]' '1.0 · source · ' +// @has - '//div[@class="main-heading"]/*[@class="out-of-band"]' '1.0.0 · ' +// @!has - '//div[@class="main-heading"]/*[@class="out-of-band"]' '1.0.0 · source · ' #[stable(feature = "bar", since = "1.0")] pub fn foo() {} // @has foo/struct.Bar.html -// @has - '//div[@class="main-heading"]/*[@class="out-of-band"]' '1.0 · ' -// @!has - '//div[@class="main-heading"]/*[@class="out-of-band"]' '1.0 · source · ' +// @has - '//div[@class="main-heading"]/*[@class="out-of-band"]' '1.0.0 · ' +// @!has - '//div[@class="main-heading"]/*[@class="out-of-band"]' '1.0.0 · source · ' #[stable(feature = "bar", since = "1.0")] pub struct Bar; impl Bar { - // @has - '//*[@id="method.bar"]/*[@class="since rightside"]' '2.0' - // @!has - '//*[@id="method.bar"]/*[@class="rightside"]' '2.0 ·' + // @has - '//*[@id="method.bar"]/*[@class="since rightside"]' '2.0.0' + // @!has - '//*[@id="method.bar"]/*[@class="rightside"]' '2.0.0 ·' #[stable(feature = "foobar", since = "2.0")] pub fn bar() {} } From fd8907f9cfcebcf0a8ae182a9f7006b494ef1d5d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 24 Oct 2023 17:57:02 -0700 Subject: [PATCH 5/7] Handle structured stable attribute 'since' version in clippy --- .../clippy_utils/src/qualify_min_const_fn.rs | 31 ++++++++++++------- 1 file changed, 19 insertions(+), 12 deletions(-) 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 f6096ea546d..514988c6744 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,6 +5,7 @@ use crate::msrvs::Msrv; use hir::LangItem; +use rustc_attr::{rust_version_symbol, Since}; use rustc_const_eval::transform::check_consts::ConstCx; use rustc_hir as hir; use rustc_hir::def_id::DefId; @@ -370,19 +371,25 @@ fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: &Msrv) -> bool { // function could be removed if `rustc` provided a MSRV-aware version of `is_const_fn`. // as a part of an unimplemented MSRV check https://github.com/rust-lang/rust/issues/65262. - // 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 = since - .as_str() - .split('-') - .next() - .expect("rustc_attr::StabilityLevel::Stable::since` is empty"); + 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 current_rustc_version = rust_version_symbol(); + let short_version = current_rustc_version.as_str().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::Err => return false, + }; - let since = rustc_span::Symbol::intern(short_version); - - msrv.meets(RustcVersion::parse(since.as_str()).unwrap_or_else(|err| { - panic!("`rustc_attr::StabilityLevel::Stable::since` is ill-formatted: `{since}`, {err:?}") - })) + msrv.meets(const_stab_rust_version) } else { // Unstable const fn with the feature enabled. msrv.current().is_none() From 51f7fbab92ec13c660c500be863c3f91c2648e62 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 24 Oct 2023 18:04:45 -0700 Subject: [PATCH 6/7] CFG_RELEASE is mandatory, no need for option_env The same file already contains another env!("CFG_RELEASE") on line 632, so it's impossible for this crate to compile without CFG_RELEASE set. --- compiler/rustc_attr/src/builtin.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs index a31b683b1af..c43042bd85e 100644 --- a/compiler/rustc_attr/src/builtin.rs +++ b/compiler/rustc_attr/src/builtin.rs @@ -25,8 +25,7 @@ use crate::session_diagnostics::{self, IncorrectReprFormatGenericCause}; pub const VERSION_PLACEHOLDER: &str = "CURRENT_RUSTC_VERSION"; pub fn rust_version_symbol() -> Symbol { - let version = option_env!("CFG_RELEASE").unwrap_or(""); - Symbol::intern(&version) + Symbol::intern(env!("CFG_RELEASE")) } pub fn is_builtin_attr(attr: &Attribute) -> bool { From 1a9ea1f1a591915ef58931496d998871b8dd5524 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 24 Oct 2023 18:06:57 -0700 Subject: [PATCH 7/7] Expose a non-Symbol way to access current rustc version string --- compiler/rustc_attr/src/builtin.rs | 6 ++++-- src/librustdoc/html/render/mod.rs | 4 ++-- src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs | 7 +++---- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs index c43042bd85e..bd85483885e 100644 --- a/compiler/rustc_attr/src/builtin.rs +++ b/compiler/rustc_attr/src/builtin.rs @@ -24,8 +24,10 @@ 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 rust_version_symbol() -> Symbol { - Symbol::intern(env!("CFG_RELEASE")) + Symbol::intern(CURRENT_RUSTC_VERSION) } pub fn is_builtin_attr(attr: &Attribute) -> bool { @@ -629,7 +631,7 @@ pub fn eval_condition( sess.emit_warning(session_diagnostics::UnknownVersionLiteral { span: *span }); return false; }; - let rustc_version = parse_version(env!("CFG_RELEASE"), true).unwrap(); + 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 { diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 1aad014c571..e01341acf43 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -48,7 +48,7 @@ use std::str; use std::string::ToString; use askama::Template; -use rustc_attr::{rust_version_symbol, ConstStability, Deprecation, Since, StabilityLevel}; +use rustc_attr::{ConstStability, Deprecation, Since, StabilityLevel, CURRENT_RUSTC_VERSION}; use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::def_id::{DefId, DefIdSet}; @@ -979,7 +979,7 @@ fn render_stability_since_raw_with_extra( fn since_to_string(since: &Since) -> Option { match since { Since::Version(since) => Some(since.to_string()), - Since::Current => Some(rust_version_symbol().to_string()), + Since::Current => Some(CURRENT_RUSTC_VERSION.to_owned()), 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 514988c6744..1e465ac91b7 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::{rust_version_symbol, Since}; +use rustc_attr::{Since, CURRENT_RUSTC_VERSION}; use rustc_const_eval::transform::check_consts::ConstCx; use rustc_hir as hir; use rustc_hir::def_id::DefId; @@ -380,10 +380,9 @@ fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: &Msrv) -> bool { 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 current_rustc_version = rust_version_symbol(); - let short_version = current_rustc_version.as_str().split('-').next().unwrap(); + 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:?}") + panic!("`rustc_attr::StabilityLevel::Stable::since` is ill-formatted: `{CURRENT_RUSTC_VERSION}`, {err:?}") }) }, Since::Err => return false,