From 6933a671d32f6f8077404e6d558c7ba60bdbbfb1 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 24 Oct 2023 16:35:43 -0700 Subject: [PATCH] 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() {} }