From 9a0c85655c7b60c5b6f595e98f72275e8052058b Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Mon, 12 May 2025 18:43:00 +0200 Subject: [PATCH 01/18] Specify that split_ascii_whitespace uses the same definition as is_ascii_whitespace --- library/core/src/str/mod.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index dafabba645c61..0123e52f5470c 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -1172,6 +1172,7 @@ impl str { /// The iterator returned will return string slices that are sub-slices of /// the original string slice, separated by any amount of ASCII whitespace. /// + /// This uses the same definition as [`char::is_ascii_whitespace`]. /// To split by Unicode `Whitespace` instead, use [`split_whitespace`]. /// /// [`split_whitespace`]: str::split_whitespace @@ -1190,7 +1191,8 @@ impl str { /// assert_eq!(None, iter.next()); /// ``` /// - /// All kinds of ASCII whitespace are considered: + /// Various kinds of ASCII whitespace are considered + /// (see [`char::is_ascii_whitespace`]): /// /// ``` /// let mut iter = " Mary had\ta little \n\t lamb".split_ascii_whitespace(); From 8f0522dc811b7132c3bd2b2bc5c134a869824cf3 Mon Sep 17 00:00:00 2001 From: teor Date: Mon, 12 May 2025 18:49:36 +1000 Subject: [PATCH 02/18] Fix confusing WTF surrogate safety docs --- library/std/src/ffi/os_str.rs | 6 +++--- library/std/src/sys/os_str/wtf8.rs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/library/std/src/ffi/os_str.rs b/library/std/src/ffi/os_str.rs index 72bdf03ee61a4..7f0033c196213 100644 --- a/library/std/src/ffi/os_str.rs +++ b/library/std/src/ffi/os_str.rs @@ -594,9 +594,9 @@ impl OsString { /// The slice must be valid for the platform encoding (as described in /// [`OsStr::from_encoded_bytes_unchecked`]). /// - /// This bypasses the encoding-dependent surrogate joining, so `self` must - /// not end with a leading surrogate half and `other` must not start with - /// with a trailing surrogate half. + /// This bypasses the encoding-dependent surrogate joining, so either + /// `self` must not end with a leading surrogate half, or `other` must not + /// start with a trailing surrogate half. #[inline] pub(crate) unsafe fn extend_from_slice_unchecked(&mut self, other: &[u8]) { // SAFETY: Guaranteed by caller. diff --git a/library/std/src/sys/os_str/wtf8.rs b/library/std/src/sys/os_str/wtf8.rs index 5174ea65d0cd9..892bd2e3de659 100644 --- a/library/std/src/sys/os_str/wtf8.rs +++ b/library/std/src/sys/os_str/wtf8.rs @@ -215,9 +215,9 @@ impl Buf { /// The slice must be valid for the platform encoding (as described in /// [`Slice::from_encoded_bytes_unchecked`]). /// - /// This bypasses the WTF-8 surrogate joining, so `self` must not end with a - /// leading surrogate half and `other` must not start with with a trailing - /// surrogate half. + /// This bypasses the WTF-8 surrogate joining, so either `self` must not + /// end with a leading surrogate half, or `other` must not start with a + /// trailing surrogate half. #[inline] pub unsafe fn extend_from_slice_unchecked(&mut self, other: &[u8]) { self.inner.extend_from_slice(other); From e139d268f0d863fb45111b64dff7c981a377c8af Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 1 May 2025 16:09:53 +1000 Subject: [PATCH 03/18] Introduce some typedefs to improve readability. --- compiler/rustc_attr_parsing/src/context.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 55c3df003fe16..6edb9133eabda 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -26,12 +26,16 @@ macro_rules! attribute_groups { ( pub(crate) static $name: ident = [$($names: ty),* $(,)?]; ) => { - pub(crate) static $name: LazyLock<( - BTreeMap<&'static [Symbol], Vec, &ArgParser<'_>) + Send + Sync>>>, - Vec) -> Option>> - )> = LazyLock::new(|| { - let mut accepts = BTreeMap::<_, Vec, &ArgParser<'_>) + Send + Sync>>>::new(); - let mut finalizes = Vec::) -> Option>>::new(); + type Accepts = BTreeMap< + &'static [Symbol], + Vec, &ArgParser<'_>)>> + >; + type Finalizes = Vec< + Box) -> Option> + >; + pub(crate) static $name: LazyLock<(Accepts, Finalizes)> = LazyLock::new(|| { + let mut accepts = Accepts::new(); + let mut finalizes = Finalizes::new(); $( { thread_local! { From 5b808b7da8df062f1c4d5d73c98e19e6cc4b283b Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 1 May 2025 16:09:16 +1000 Subject: [PATCH 04/18] Simplify `Accepts`. There only needs to be one `Fn` per symbol, not multiple. --- compiler/rustc_attr_parsing/src/context.rs | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 6edb9133eabda..aef803c7442c6 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -28,7 +28,7 @@ macro_rules! attribute_groups { ) => { type Accepts = BTreeMap< &'static [Symbol], - Vec, &ArgParser<'_>)>> + Box, &ArgParser<'_>)> >; type Finalizes = Vec< Box) -> Option> @@ -43,11 +43,12 @@ macro_rules! attribute_groups { }; for (k, v) in <$names>::ATTRIBUTES { - accepts.entry(*k).or_default().push(Box::new(|cx, args| { + let old = accepts.insert(*k, Box::new(|cx, args| { STATE_OBJECT.with_borrow_mut(|s| { v(s, cx, args) }) })); + assert!(old.is_none()); } finalizes.push(Box::new(|cx| { @@ -267,15 +268,11 @@ impl<'sess> AttributeParser<'sess> { let (path, args) = parser.deconstruct(); let parts = path.segments().map(|i| i.name).collect::>(); - if let Some(accepts) = ATTRIBUTE_MAPPING.0.get(parts.as_slice()) { - for f in accepts { - let cx = AcceptContext { - group_cx: &group_cx, - attr_span: lower_span(attr.span), - }; + if let Some(accept) = ATTRIBUTE_MAPPING.0.get(parts.as_slice()) { + let cx = + AcceptContext { group_cx: &group_cx, attr_span: lower_span(attr.span) }; - f(&cx, &args) - } + accept(&cx, &args) } else { // if we're here, we must be compiling a tool attribute... Or someone forgot to // parse their fancy new attribute. Let's warn them in any case. If you are that From 1525f548bc376b04692b97a84898900e8f27fdd0 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 1 May 2025 16:24:48 +1000 Subject: [PATCH 05/18] Fix up some comments. Some are too long (> 100 chars), some are too short, some are missing full stops, some are missing upper-case letters at the start of sentences. --- compiler/rustc_attr_parsing/src/context.rs | 31 +++++++++++----------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index aef803c7442c6..1360fc6871422 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -115,7 +115,8 @@ impl<'a> Deref for AcceptContext<'a> { /// Context given to every attribute parser during finalization. /// -/// Gives [`AttributeParser`](crate::attributes::AttributeParser)s enough information to create errors, for example. +/// Gives [`AttributeParser`](crate::attributes::AttributeParser)s enough information to create +/// errors, for example. pub(crate) struct FinalizeContext<'a> { /// The parse context, gives access to the session and the /// diagnostics context. @@ -146,10 +147,9 @@ pub struct AttributeParser<'sess> { sess: &'sess Session, features: Option<&'sess Features>, - /// *only* parse attributes with this symbol. + /// *Only* parse attributes with this symbol. /// - /// Used in cases where we want the lowering infrastructure for - /// parse just a single attribute. + /// Used in cases where we want the lowering infrastructure for parse just a single attribute. parse_only: Option, /// Can be used to instruct parsers to reduce the number of diagnostics it emits. @@ -162,9 +162,9 @@ impl<'sess> AttributeParser<'sess> { /// One example where this is necessary, is to parse `feature` attributes themselves for /// example. /// - /// Try to use this as little as possible. Attributes *should* be lowered during `rustc_ast_lowering`. - /// Some attributes require access to features to parse, which would crash if you tried to do so - /// through [`parse_limited`](Self::parse_limited). + /// Try to use this as little as possible. Attributes *should* be lowered during + /// `rustc_ast_lowering`. Some attributes require access to features to parse, which would + /// crash if you tried to do so through [`parse_limited`](Self::parse_limited). /// /// To make sure use is limited, supply a `Symbol` you'd like to parse. Only attributes with /// that symbol are picked out of the list of instructions and parsed. Those are returned. @@ -222,19 +222,18 @@ impl<'sess> AttributeParser<'sess> { let group_cx = FinalizeContext { cx: self, target_span }; for attr in attrs { - // if we're only looking for a single attribute, - // skip all the ones we don't care about + // If we're only looking for a single attribute, skip all the ones we don't care about. if let Some(expected) = self.parse_only { if !attr.has_name(expected) { continue; } } - // sometimes, for example for `#![doc = include_str!("readme.md")]`, + // Sometimes, for example for `#![doc = include_str!("readme.md")]`, // doc still contains a non-literal. You might say, when we're lowering attributes // that's expanded right? But no, sometimes, when parsing attributes on macros, // we already use the lowering logic and these are still there. So, when `omit_doc` - // is set we *also* want to ignore these + // is set we *also* want to ignore these. if omit_doc == OmitDoc::Skip && attr.has_name(sym::doc) { continue; } @@ -274,11 +273,11 @@ impl<'sess> AttributeParser<'sess> { accept(&cx, &args) } else { - // if we're here, we must be compiling a tool attribute... Or someone forgot to - // parse their fancy new attribute. Let's warn them in any case. If you are that - // person, and you really your attribute should remain unparsed, carefully read the - // documentation in this module and if you still think so you can add an exception - // to this assertion. + // If we're here, we must be compiling a tool attribute... Or someone + // forgot to parse their fancy new attribute. Let's warn them in any case. + // If you are that person, and you really think your attribute should + // remain unparsed, carefully read the documentation in this module and if + // you still think so you can add an exception to this assertion. // FIXME(jdonszelmann): convert other attributes, and check with this that // we caught em all From 354b1cbcca13dc85bd0a57f8c2b254831394b6fb Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 20 May 2025 09:20:27 +1000 Subject: [PATCH 06/18] Avoid `rustc_span::` qualifiers. In several files they are entirely unnecessary, with the relevant names already imported. And in a few I have added the necessary `use` item. --- .../rustc_attr_parsing/src/attributes/allow_unstable.rs | 4 ++-- compiler/rustc_attr_parsing/src/attributes/deprecation.rs | 4 ++-- compiler/rustc_attr_parsing/src/attributes/mod.rs | 8 ++++---- compiler/rustc_attr_parsing/src/attributes/repr.rs | 6 +++--- compiler/rustc_attr_parsing/src/attributes/stability.rs | 2 +- .../rustc_attr_parsing/src/attributes/transparency.rs | 6 +++--- compiler/rustc_attr_parsing/src/parser.rs | 3 +-- 7 files changed, 16 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs b/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs index c1d95d07f4c65..b9b6ca261193f 100644 --- a/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs +++ b/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs @@ -10,7 +10,7 @@ use crate::session_diagnostics; pub(crate) struct AllowInternalUnstableParser; impl CombineAttributeParser for AllowInternalUnstableParser { - const PATH: &'static [rustc_span::Symbol] = &[sym::allow_internal_unstable]; + const PATH: &'static [Symbol] = &[sym::allow_internal_unstable]; type Item = (Symbol, Span); const CONVERT: ConvertFn = AttributeKind::AllowInternalUnstable; @@ -24,7 +24,7 @@ impl CombineAttributeParser for AllowInternalUnstableParser { pub(crate) struct AllowConstFnUnstableParser; impl CombineAttributeParser for AllowConstFnUnstableParser { - const PATH: &'static [rustc_span::Symbol] = &[sym::rustc_allow_const_fn_unstable]; + const PATH: &'static [Symbol] = &[sym::rustc_allow_const_fn_unstable]; type Item = Symbol; const CONVERT: ConvertFn = AttributeKind::AllowConstFnUnstable; diff --git a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs index fb3d5f57d4fac..1775770ec680e 100644 --- a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs +++ b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs @@ -42,9 +42,9 @@ fn get( } impl SingleAttributeParser for DeprecationParser { - const PATH: &'static [rustc_span::Symbol] = &[sym::deprecated]; + const PATH: &'static [Symbol] = &[sym::deprecated]; - fn on_duplicate(cx: &AcceptContext<'_>, first_span: rustc_span::Span) { + fn on_duplicate(cx: &AcceptContext<'_>, first_span: Span) { // FIXME(jdonszelmann): merge with errors from check_attrs.rs cx.emit_err(session_diagnostics::UnusedMultiple { this: cx.attr_span, diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index 6ecd6b4d7dbb7..f45cf984f7191 100644 --- a/compiler/rustc_attr_parsing/src/attributes/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -17,7 +17,7 @@ use std::marker::PhantomData; use rustc_attr_data_structures::AttributeKind; -use rustc_span::Span; +use rustc_span::{Span, Symbol}; use thin_vec::ThinVec; use crate::context::{AcceptContext, FinalizeContext}; @@ -33,7 +33,7 @@ pub(crate) mod transparency; pub(crate) mod util; type AcceptFn = fn(&mut T, &AcceptContext<'_>, &ArgParser<'_>); -type AcceptMapping = &'static [(&'static [rustc_span::Symbol], AcceptFn)]; +type AcceptMapping = &'static [(&'static [Symbol], AcceptFn)]; /// An [`AttributeParser`] is a type which searches for syntactic attributes. /// @@ -72,7 +72,7 @@ pub(crate) trait AttributeParser: Default + 'static { /// [`SingleAttributeParser`] can only convert attributes one-to-one, and cannot combine multiple /// attributes together like is necessary for `#[stable()]` and `#[unstable()]` for example. pub(crate) trait SingleAttributeParser: 'static { - const PATH: &'static [rustc_span::Symbol]; + const PATH: &'static [Symbol]; /// Caled when a duplicate attribute is found. /// @@ -119,7 +119,7 @@ type ConvertFn = fn(ThinVec) -> AttributeKind; /// [`CombineAttributeParser`] can only convert a single kind of attribute, and cannot combine multiple /// attributes together like is necessary for `#[stable()]` and `#[unstable()]` for example. pub(crate) trait CombineAttributeParser: 'static { - const PATH: &'static [rustc_span::Symbol]; + const PATH: &'static [Symbol]; type Item; const CONVERT: ConvertFn; diff --git a/compiler/rustc_attr_parsing/src/attributes/repr.rs b/compiler/rustc_attr_parsing/src/attributes/repr.rs index 43dfb85a7c411..ab523ce0038da 100644 --- a/compiler/rustc_attr_parsing/src/attributes/repr.rs +++ b/compiler/rustc_attr_parsing/src/attributes/repr.rs @@ -1,7 +1,7 @@ use rustc_abi::Align; use rustc_ast::{IntTy, LitIntType, LitKind, UintTy}; use rustc_attr_data_structures::{AttributeKind, IntType, ReprAttr}; -use rustc_span::{Span, Symbol, sym}; +use rustc_span::{DUMMY_SP, Span, Symbol, sym}; use super::{CombineAttributeParser, ConvertFn}; use crate::context::AcceptContext; @@ -21,7 +21,7 @@ pub(crate) struct ReprParser; impl CombineAttributeParser for ReprParser { type Item = (ReprAttr, Span); - const PATH: &'static [rustc_span::Symbol] = &[sym::repr]; + const PATH: &'static [Symbol] = &[sym::repr]; const CONVERT: ConvertFn = AttributeKind::Repr; fn extend<'a>( @@ -99,7 +99,7 @@ fn parse_repr(cx: &AcceptContext<'_>, param: &MetaItemParser<'_>) -> Option, _first_span: Span) {} diff --git a/compiler/rustc_attr_parsing/src/attributes/transparency.rs b/compiler/rustc_attr_parsing/src/attributes/transparency.rs index ce42b0507ed57..d229fc0974010 100644 --- a/compiler/rustc_attr_parsing/src/attributes/transparency.rs +++ b/compiler/rustc_attr_parsing/src/attributes/transparency.rs @@ -1,6 +1,6 @@ use rustc_attr_data_structures::AttributeKind; use rustc_span::hygiene::Transparency; -use rustc_span::sym; +use rustc_span::{Span, Symbol, sym}; use super::{AcceptContext, SingleAttributeParser}; use crate::parser::ArgParser; @@ -11,9 +11,9 @@ pub(crate) struct TransparencyParser; #[allow(rustc::untranslatable_diagnostic)] #[allow(rustc::diagnostic_outside_of_impl)] impl SingleAttributeParser for TransparencyParser { - const PATH: &'static [rustc_span::Symbol] = &[sym::rustc_macro_transparency]; + const PATH: &'static [Symbol] = &[sym::rustc_macro_transparency]; - fn on_duplicate(cx: &crate::context::AcceptContext<'_>, first_span: rustc_span::Span) { + fn on_duplicate(cx: &crate::context::AcceptContext<'_>, first_span: Span) { cx.dcx().span_err(vec![first_span, cx.attr_span], "multiple macro transparency attributes"); } diff --git a/compiler/rustc_attr_parsing/src/parser.rs b/compiler/rustc_attr_parsing/src/parser.rs index 077d953cfa318..f433d3574e186 100644 --- a/compiler/rustc_attr_parsing/src/parser.rs +++ b/compiler/rustc_attr_parsing/src/parser.rs @@ -12,8 +12,7 @@ use rustc_ast::{AttrArgs, DelimArgs, Expr, ExprKind, LitKind, MetaItemLit, Norma use rustc_ast_pretty::pprust; use rustc_errors::DiagCtxtHandle; use rustc_hir::{self as hir, AttrPath}; -use rustc_span::symbol::{Ident, kw, sym}; -use rustc_span::{ErrorGuaranteed, Span, Symbol}; +use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol, kw, sym}; pub struct SegmentIterator<'a> { offset: usize, From ec5e841957c2f0200496aa551823157d92a19cc5 Mon Sep 17 00:00:00 2001 From: MarcoIeni <11428655+MarcoIeni@users.noreply.github.com> Date: Sat, 24 May 2025 15:12:14 +0200 Subject: [PATCH 07/18] ci: move PR job x86_64-gnu-tools to codebuild --- src/ci/github-actions/jobs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml index 2daa862460534..1d175bd97e6a1 100644 --- a/src/ci/github-actions/jobs.yml +++ b/src/ci/github-actions/jobs.yml @@ -123,7 +123,7 @@ pr: DOCKER_SCRIPT: x86_64-gnu-llvm.sh <<: *job-linux-16c - name: x86_64-gnu-tools - <<: *job-linux-16c + <<: *job-linux-36c-codebuild # Jobs that run when you perform a try build (@bors try) # These jobs automatically inherit envs.try, to avoid repeating From 6d47489e568a80b2c29c56b3664cc41c30a8f8fa Mon Sep 17 00:00:00 2001 From: Fluid <90795031+fluiderson@users.noreply.github.com> Date: Fri, 23 May 2025 23:57:38 +0300 Subject: [PATCH 08/18] improve the `std::fs::create_dir_all` docs related to atomicity --- library/std/src/fs.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 509e673bdb8b9..8047c0c03ad4f 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -2803,8 +2803,8 @@ pub fn create_dir>(path: P) -> io::Result<()> { /// Recursively create a directory and all of its parent components if they /// are missing. /// -/// If this function returns an error, some of the parent components might have -/// been created already. +/// This function is not atomic. If it returns an error, any parent components it was able to create +/// will remain. /// /// If the empty path is passed to this function, it always succeeds without /// creating any directories. From c299e297eec214580c2dad56b8b1cd78bd989883 Mon Sep 17 00:00:00 2001 From: Chris Denton Date: Mon, 23 Dec 2024 15:09:51 +0000 Subject: [PATCH 09/18] Implement normalize lexically --- library/std/src/path.rs | 77 +++++++++++++++++++++++++++++++++++++++ library/std/tests/path.rs | 56 +++++++++++++++++++++++++++- 2 files changed, 132 insertions(+), 1 deletion(-) diff --git a/library/std/src/path.rs b/library/std/src/path.rs index 7959c63385816..2cdded1dfcf99 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -2154,6 +2154,13 @@ pub struct Path { #[stable(since = "1.7.0", feature = "strip_prefix")] pub struct StripPrefixError(()); +/// An error returned from [`Path::normalize_lexically`] if a `..` parent reference +/// would escape the path. +#[unstable(feature = "normalize_lexically", issue = "134694")] +#[derive(Debug, PartialEq)] +#[non_exhaustive] +pub struct NormalizeError; + impl Path { // The following (private!) function allows construction of a path from a u8 // slice, which is only safe when it is known to follow the OsStr encoding. @@ -2961,6 +2968,67 @@ impl Path { fs::canonicalize(self) } + /// Normalize a path, including `..` without traversing the filesystem. + /// + /// Returns an error if normalization would leave leading `..` components. + /// + ///
+ /// + /// This function always resolves `..` to the "lexical" parent. + /// That is "a/b/../c" will always resolve to `a/c` which can change the meaning of the path. + /// In particular, `a/c` and `a/b/../c` are distinct on many systems because `b` may be a symbolic link, so its parent isn’t `a`. + /// + ///
+ /// + /// [`path::absolute`](absolute) is an alternative that preserves `..`. + /// Or [`Path::canonicalize`] can be used to resolve any `..` by querying the filesystem. + #[unstable(feature = "normalize_lexically", issue = "134694")] + pub fn normalize_lexically(&self) -> Result { + let mut lexical = PathBuf::new(); + let mut iter = self.components().peekable(); + + // Find the root, if any, and add it to the lexical path. + // Here we treat the Windows path "C:\" as a single "root" even though + // `components` splits it into two: (Prefix, RootDir). + let root = match iter.peek() { + Some(Component::ParentDir) => return Err(NormalizeError), + Some(p @ Component::RootDir) | Some(p @ Component::CurDir) => { + lexical.push(p); + iter.next(); + lexical.as_os_str().len() + } + Some(Component::Prefix(prefix)) => { + lexical.push(prefix.as_os_str()); + iter.next(); + if let Some(p @ Component::RootDir) = iter.peek() { + lexical.push(p); + iter.next(); + } + lexical.as_os_str().len() + } + None => return Ok(PathBuf::new()), + Some(Component::Normal(_)) => 0, + }; + + for component in iter { + match component { + Component::RootDir => unreachable!(), + Component::Prefix(_) => return Err(NormalizeError), + Component::CurDir => continue, + Component::ParentDir => { + // It's an error if ParentDir causes us to go above the "root". + if lexical.as_os_str().len() == root { + return Err(NormalizeError); + } else { + lexical.pop(); + } + } + Component::Normal(path) => lexical.push(path), + } + } + Ok(lexical) + } + /// Reads a symbolic link, returning the file that the link points to. /// /// This is an alias to [`fs::read_link`]. @@ -3502,6 +3570,15 @@ impl Error for StripPrefixError { } } +#[unstable(feature = "normalize_lexically", issue = "134694")] +impl fmt::Display for NormalizeError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("parent reference `..` points outside of base directory") + } +} +#[unstable(feature = "normalize_lexically", issue = "134694")] +impl Error for NormalizeError {} + /// Makes the path absolute without accessing the filesystem. /// /// If the path is relative, the current directory is used as the base directory. diff --git a/library/std/tests/path.rs b/library/std/tests/path.rs index 87e0d226cbd31..781855a2d14aa 100644 --- a/library/std/tests/path.rs +++ b/library/std/tests/path.rs @@ -3,7 +3,8 @@ path_add_extension, path_file_prefix, maybe_uninit_slice, - os_string_pathbuf_leak + os_string_pathbuf_leak, + normalize_lexically )] use std::clone::CloneToUninit; @@ -2007,3 +2008,56 @@ fn test_embedded_newline() { assert_eq!(path.file_name(), Some(OsStr::new("foo\nbar"))); assert_eq!(path.to_str(), Some("foo\nbar")); } + +#[test] +fn normalize_lexically() { + #[track_caller] + fn check_ok(a: &str, b: &str) { + assert_eq!(Path::new(a).normalize_lexically().unwrap(), PathBuf::from(b)); + } + + #[track_caller] + fn check_err(a: &str) { + assert!(Path::new(a).normalize_lexically().is_err()); + } + + // Relative paths + check_ok("a", "a"); + check_ok("./a", "./a"); + check_ok("a/b/c", "a/b/c"); + check_ok("a/././b/./c/.", "a/b/c"); + check_ok("a/../c", "c"); + check_ok("./a/b", "./a/b"); + check_ok("a/../b/c/..", "b"); + + check_err(".."); + check_err("../.."); + check_err("a/../.."); + check_err("a/../../b"); + check_err("a/../../b/c"); + check_err("a/../b/../.."); + + // Check we don't escape the root or prefix + #[cfg(unix)] + { + check_err("/.."); + check_err("/a/../.."); + } + #[cfg(windows)] + { + check_err(r"C:\.."); + check_err(r"C:\a\..\.."); + + check_err(r"C:.."); + check_err(r"C:a\..\.."); + + check_err(r"\\server\share\.."); + check_err(r"\\server\share\a\..\.."); + + check_err(r"\.."); + check_err(r"\a\..\.."); + + check_err(r"\\?\UNC\server\share\.."); + check_err(r"\\?\UNC\server\share\a\..\.."); + } +} From 5f857a9871240efe6e15ca94dd03e06b90c6413d Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 9 May 2025 15:40:56 +0200 Subject: [PATCH 10/18] Rename `clean::Enum::variants` method into `non_stripped_variants` --- src/librustdoc/clean/types.rs | 2 +- src/librustdoc/html/render/print_item.rs | 2 +- src/librustdoc/html/render/sidebar.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 07ecd98f77596..f353ad5cf917e 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -2107,7 +2107,7 @@ impl Enum { self.variants.iter().any(|f| f.is_stripped()) } - pub(crate) fn variants(&self) -> impl Iterator { + pub(crate) fn non_stripped_variants(&self) -> impl Iterator { self.variants.iter().filter(|v| !v.is_stripped()) } } diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index 39a631b637bd4..552e67c9f53d9 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -1535,7 +1535,7 @@ fn print_tuple_struct_fields(cx: &Context<'_>, s: &[clean::Item]) -> impl Displa fn item_enum(cx: &Context<'_>, it: &clean::Item, e: &clean::Enum) -> impl fmt::Display { fmt::from_fn(|w| { - let count_variants = e.variants().count(); + let count_variants = e.non_stripped_variants().count(); wrap_item(w, |w| { render_attributes_in_code(w, it, cx); write!( diff --git a/src/librustdoc/html/render/sidebar.rs b/src/librustdoc/html/render/sidebar.rs index 361966325fb3c..91540e06e3398 100644 --- a/src/librustdoc/html/render/sidebar.rs +++ b/src/librustdoc/html/render/sidebar.rs @@ -599,7 +599,7 @@ fn sidebar_enum<'a>( deref_id_map: &'a DefIdMap, ) { let mut variants = e - .variants() + .non_stripped_variants() .filter_map(|v| v.name) .map(|name| Link::new(format!("variant.{name}"), name.to_string())) .collect::>(); From 560aec13ba62c1a8fd7b2f9fbeada61096809613 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 9 May 2025 17:10:38 +0200 Subject: [PATCH 11/18] Unify rendering of type aliases without ADT items --- src/librustdoc/clean/types.rs | 13 + src/librustdoc/html/render/print_item.rs | 268 +++++++++--------- src/librustdoc/html/templates/item_union.html | 7 +- 3 files changed, 151 insertions(+), 137 deletions(-) diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index f353ad5cf917e..98d57494dbe68 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -610,6 +610,9 @@ impl Item { UnionItem(ref union_) => Some(union_.has_stripped_entries()), EnumItem(ref enum_) => Some(enum_.has_stripped_entries()), VariantItem(ref v) => v.has_stripped_entries(), + TypeAliasItem(ref type_alias) => { + type_alias.inner_type.as_ref().and_then(|t| t.has_stripped_entries()) + } _ => None, } } @@ -2345,6 +2348,16 @@ pub(crate) enum TypeAliasInnerType { Struct { ctor_kind: Option, fields: Vec }, } +impl TypeAliasInnerType { + fn has_stripped_entries(&self) -> Option { + Some(match self { + Self::Enum { variants, .. } => variants.iter().any(|v| v.is_stripped()), + Self::Union { fields } => fields.iter().any(|f| f.is_stripped()), + Self::Struct { fields, .. } => fields.iter().any(|f| f.is_stripped()), + }) + } +} + #[derive(Clone, Debug)] pub(crate) struct TypeAlias { pub(crate) type_: Type, diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index 552e67c9f53d9..616d89bca6a73 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -1278,94 +1278,40 @@ fn item_type_alias(cx: &Context<'_>, it: &clean::Item, t: &clean::TypeAlias) -> match inner_type { clean::TypeAliasInnerType::Enum { variants, is_non_exhaustive } => { - let variants_iter = || variants.iter().filter(|i| !i.is_stripped()); let ty = cx.tcx().type_of(it.def_id().unwrap()).instantiate_identity(); let enum_def_id = ty.ty_adt_def().unwrap().did(); - wrap_item(w, |w| { - let variants_len = variants.len(); - let variants_count = variants_iter().count(); - let has_stripped_entries = variants_len != variants_count; - - write!( - w, - "enum {}{}{}", - it.name.unwrap(), - t.generics.print(cx), - render_enum_fields( - cx, - Some(&t.generics), - variants, - variants_count, - has_stripped_entries, - *is_non_exhaustive, - enum_def_id, - ) - ) - })?; - write!(w, "{}", item_variants(cx, it, variants, enum_def_id))?; + DisplayEnum { + variants, + generics: &t.generics, + is_non_exhaustive: *is_non_exhaustive, + def_id: enum_def_id, + } + .render_into(cx, it, false, w)?; } clean::TypeAliasInnerType::Union { fields } => { - wrap_item(w, |w| { - let fields_count = fields.iter().filter(|i| !i.is_stripped()).count(); - let has_stripped_fields = fields.len() != fields_count; - - write!( - w, - "union {}{}{}", - it.name.unwrap(), - t.generics.print(cx), - render_struct_fields( - Some(&t.generics), - None, - fields, - "", - true, - has_stripped_fields, - cx, - ), - ) - })?; - write!(w, "{}", item_fields(cx, it, fields, None))?; + ItemUnion { cx, it, fields, generics: &t.generics, document_union: false } + .render_into(w)?; } clean::TypeAliasInnerType::Struct { ctor_kind, fields } => { - wrap_item(w, |w| { - let fields_count = fields.iter().filter(|i| !i.is_stripped()).count(); - let has_stripped_fields = fields.len() != fields_count; - - write!( - w, - "struct {}{}{}", - it.name.unwrap(), - t.generics.print(cx), - render_struct_fields( - Some(&t.generics), - *ctor_kind, - fields, - "", - true, - has_stripped_fields, - cx, - ), - ) - })?; - write!(w, "{}", item_fields(cx, it, fields, None))?; + DisplayStruct { ctor_kind: *ctor_kind, generics: &t.generics, fields } + .render_into(cx, it, false, w)?; } } + } else { + let def_id = it.item_id.expect_def_id(); + // Render any items associated directly to this alias, as otherwise they + // won't be visible anywhere in the docs. It would be nice to also show + // associated items from the aliased type (see discussion in #32077), but + // we need #14072 to make sense of the generics. + write!( + w, + "{}{}", + render_assoc_items(cx, it, def_id, AssocItemRender::All), + document_type_layout(cx, def_id) + )?; } - let def_id = it.item_id.expect_def_id(); - // Render any items associated directly to this alias, as otherwise they - // won't be visible anywhere in the docs. It would be nice to also show - // associated items from the aliased type (see discussion in #32077), but - // we need #14072 to make sense of the generics. - write!( - w, - "{}{}", - render_assoc_items(cx, it, def_id, AssocItemRender::All), - document_type_layout(cx, def_id) - )?; - // [RUSTDOCIMPL] type.impl // // Include type definitions from the alias target type. @@ -1463,50 +1409,53 @@ fn item_type_alias(cx: &Context<'_>, it: &clean::Item, t: &clean::TypeAlias) -> }) } -fn item_union(cx: &Context<'_>, it: &clean::Item, s: &clean::Union) -> impl fmt::Display { - item_template!( - #[template(path = "item_union.html")] - struct ItemUnion<'a, 'cx> { - cx: &'a Context<'cx>, - it: &'a clean::Item, - s: &'a clean::Union, - }, - methods = [document, document_type_layout, render_attributes_in_pre, render_assoc_items] - ); - - impl<'a, 'cx: 'a> ItemUnion<'a, 'cx> { - fn render_union(&self) -> impl Display { - render_union(self.it, Some(&self.s.generics), &self.s.fields, self.cx) - } +item_template!( + #[template(path = "item_union.html")] + struct ItemUnion<'a, 'cx> { + cx: &'a Context<'cx>, + it: &'a clean::Item, + fields: &'a [clean::Item], + generics: &'a clean::Generics, + document_union: bool, + }, + methods = [document, document_type_layout, render_attributes_in_pre, render_assoc_items] +); + +impl<'a, 'cx: 'a> ItemUnion<'a, 'cx> { + fn render_union(&self) -> impl Display { + render_union(self.it, Some(&self.generics), &self.fields, self.cx) + } - fn document_field(&self, field: &'a clean::Item) -> impl Display { - document(self.cx, field, Some(self.it), HeadingOffset::H3) - } + fn document_field(&self, field: &'a clean::Item) -> impl Display { + document(self.cx, field, Some(self.it), HeadingOffset::H3) + } - fn stability_field(&self, field: &clean::Item) -> Option { - field.stability_class(self.cx.tcx()) - } + fn stability_field(&self, field: &clean::Item) -> Option { + field.stability_class(self.cx.tcx()) + } - fn print_ty(&self, ty: &'a clean::Type) -> impl Display { - ty.print(self.cx) - } + fn print_ty(&self, ty: &'a clean::Type) -> impl Display { + ty.print(self.cx) + } - fn fields_iter( - &self, - ) -> iter::Peekable> { - self.s - .fields - .iter() - .filter_map(|f| match f.kind { - clean::StructFieldItem(ref ty) => Some((f, ty)), - _ => None, - }) - .peekable() - } + fn fields_iter( + &self, + ) -> iter::Peekable> { + self.fields + .iter() + .filter_map(|f| match f.kind { + clean::StructFieldItem(ref ty) => Some((f, ty)), + _ => None, + }) + .peekable() } +} +fn item_union(cx: &Context<'_>, it: &clean::Item, s: &clean::Union) -> impl fmt::Display { fmt::from_fn(|w| { - ItemUnion { cx, it, s }.render_into(w).unwrap(); + ItemUnion { cx, it, fields: &s.fields, generics: &s.generics, document_union: true } + .render_into(w) + .unwrap(); Ok(()) }) } @@ -1533,9 +1482,25 @@ fn print_tuple_struct_fields(cx: &Context<'_>, s: &[clean::Item]) -> impl Displa }) } -fn item_enum(cx: &Context<'_>, it: &clean::Item, e: &clean::Enum) -> impl fmt::Display { - fmt::from_fn(|w| { - let count_variants = e.non_stripped_variants().count(); +struct DisplayEnum<'a> { + variants: &'a IndexVec, + generics: &'a clean::Generics, + is_non_exhaustive: bool, + def_id: DefId, +} + +impl<'a> DisplayEnum<'a> { + fn render_into( + self, + cx: &Context<'_>, + it: &clean::Item, + document_enum: bool, + w: &mut W, + ) -> fmt::Result { + let variants_count = self.variants.iter().filter(|i| !i.is_stripped()).count(); + let variants_len = self.variants.len(); + let has_stripped_entries = variants_len != variants_count; + wrap_item(w, |w| { render_attributes_in_code(w, it, cx); write!( @@ -1543,23 +1508,25 @@ fn item_enum(cx: &Context<'_>, it: &clean::Item, e: &clean::Enum) -> impl fmt::D "{}enum {}{}{}", visibility_print_with_space(it, cx), it.name.unwrap(), - e.generics.print(cx), + self.generics.print(cx), render_enum_fields( cx, - Some(&e.generics), - &e.variants, - count_variants, - e.has_stripped_entries(), - it.is_non_exhaustive(), - it.def_id().unwrap(), + Some(self.generics), + self.variants, + variants_count, + has_stripped_entries, + self.is_non_exhaustive, + self.def_id, ), ) })?; - write!(w, "{}", document(cx, it, None, HeadingOffset::H2))?; + if document_enum { + write!(w, "{}", document(cx, it, None, HeadingOffset::H2))?; + } - if count_variants != 0 { - write!(w, "{}", item_variants(cx, it, &e.variants, it.def_id().unwrap()))?; + if variants_count != 0 { + write!(w, "{}", item_variants(cx, it, self.variants, self.def_id))?; } let def_id = it.item_id.expect_def_id(); write!( @@ -1568,6 +1535,18 @@ fn item_enum(cx: &Context<'_>, it: &clean::Item, e: &clean::Enum) -> impl fmt::D render_assoc_items(cx, it, def_id, AssocItemRender::All), document_type_layout(cx, def_id) ) + } +} + +fn item_enum(cx: &Context<'_>, it: &clean::Item, e: &clean::Enum) -> impl fmt::Display { + fmt::from_fn(|w| { + DisplayEnum { + variants: &e.variants, + generics: &e.generics, + is_non_exhaustive: it.is_non_exhaustive(), + def_id: it.def_id().unwrap(), + } + .render_into(cx, it, true, w) }) } @@ -1955,27 +1934,48 @@ fn item_constant( }) } -fn item_struct(cx: &Context<'_>, it: &clean::Item, s: &clean::Struct) -> impl fmt::Display { - fmt::from_fn(|w| { +struct DisplayStruct<'a> { + ctor_kind: Option, + generics: &'a clean::Generics, + fields: &'a [clean::Item], +} + +impl<'a> DisplayStruct<'a> { + fn render_into( + self, + cx: &Context<'_>, + it: &clean::Item, + document_struct: bool, + w: &mut W, + ) -> fmt::Result { wrap_item(w, |w| { render_attributes_in_code(w, it, cx); write!( w, "{}", - render_struct(it, Some(&s.generics), s.ctor_kind, &s.fields, "", true, cx) + render_struct(it, Some(self.generics), self.ctor_kind, self.fields, "", true, cx) ) })?; - let def_id = it.item_id.expect_def_id(); + if document_struct { + write!(w, "{}", document(cx, it, None, HeadingOffset::H2))?; + } + let def_id = it.item_id.expect_def_id(); write!( w, - "{}{}{}{}", - document(cx, it, None, HeadingOffset::H2), - item_fields(cx, it, &s.fields, s.ctor_kind), + "{}{}{}", + item_fields(cx, it, self.fields, self.ctor_kind), render_assoc_items(cx, it, def_id, AssocItemRender::All), document_type_layout(cx, def_id), ) + } +} + +fn item_struct(cx: &Context<'_>, it: &clean::Item, s: &clean::Struct) -> impl fmt::Display { + fmt::from_fn(|w| { + DisplayStruct { ctor_kind: s.ctor_kind, generics: &s.generics, fields: s.fields.as_slice() } + .render_into(cx, it, true, w) }) } diff --git a/src/librustdoc/html/templates/item_union.html b/src/librustdoc/html/templates/item_union.html index b1c1d5a63a03c..f3780fb4c5baa 100644 --- a/src/librustdoc/html/templates/item_union.html +++ b/src/librustdoc/html/templates/item_union.html @@ -2,15 +2,16 @@ {{ self.render_attributes_in_pre()|safe }} {{ self.render_union()|safe }} -{{ self.document()|safe }} +{% if self.document_union %} + {{ self.document()|safe }} +{% endif %} {% if self.fields_iter().peek().is_some() %}

{# #} Fields§ {# #}

{% for (field, ty) in self.fields_iter() %} {% let name = field.name.expect("union field name") %} - {# #} + {# #} § {# #} {{ name }}: {{+ self.print_ty(ty)|safe }} {# #} From 4194745687ead479898587b85f1388635b3422e2 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 9 May 2025 17:36:32 +0200 Subject: [PATCH 12/18] Split `Item::attributes` method into three --- src/librustdoc/clean/types.rs | 145 ++++++++++++++++------------- src/librustdoc/html/render/mod.rs | 4 +- src/librustdoc/json/conversions.rs | 2 +- 3 files changed, 81 insertions(+), 70 deletions(-) diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 98d57494dbe68..1766627ce392c 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -764,14 +764,11 @@ impl Item { Some(tcx.visibility(def_id)) } - pub(crate) fn attributes(&self, tcx: TyCtxt<'_>, cache: &Cache, is_json: bool) -> Vec { + pub(crate) fn attributes_witout_repr(&self, tcx: TyCtxt<'_>, is_json: bool) -> Vec { const ALLOWED_ATTRIBUTES: &[Symbol] = &[sym::export_name, sym::link_section, sym::no_mangle, sym::non_exhaustive]; - use rustc_abi::IntegerType; - - let mut attrs: Vec = self - .attrs + self.attrs .other_attrs .iter() .filter_map(|attr| { @@ -799,74 +796,88 @@ impl Item { None } }) - .collect(); + .collect() + } - // Add #[repr(...)] - if let Some(def_id) = self.def_id() - && let ItemType::Struct | ItemType::Enum | ItemType::Union = self.type_() - { - let adt = tcx.adt_def(def_id); - let repr = adt.repr(); - let mut out = Vec::new(); - if repr.c() { - out.push("C"); - } - if repr.transparent() { - // Render `repr(transparent)` iff the non-1-ZST field is public or at least one - // field is public in case all fields are 1-ZST fields. - let render_transparent = is_json - || cache.document_private - || adt - .all_fields() - .find(|field| { - let ty = - field.ty(tcx, ty::GenericArgs::identity_for_item(tcx, field.did)); - tcx.layout_of( - ty::TypingEnv::post_analysis(tcx, field.did).as_query_input(ty), - ) - .is_ok_and(|layout| !layout.is_1zst()) - }) - .map_or_else( - || adt.all_fields().any(|field| field.vis.is_public()), - |field| field.vis.is_public(), - ); + pub(crate) fn attributes_and_repr( + &self, + tcx: TyCtxt<'_>, + cache: &Cache, + is_json: bool, + ) -> Vec { + let mut attrs = self.attributes_witout_repr(tcx, is_json); - if render_transparent { - out.push("transparent"); - } - } - if repr.simd() { - out.push("simd"); - } - let pack_s; - if let Some(pack) = repr.pack { - pack_s = format!("packed({})", pack.bytes()); - out.push(&pack_s); - } - let align_s; - if let Some(align) = repr.align { - align_s = format!("align({})", align.bytes()); - out.push(&align_s); - } - let int_s; - if let Some(int) = repr.int { - int_s = match int { - IntegerType::Pointer(is_signed) => { - format!("{}size", if is_signed { 'i' } else { 'u' }) - } - IntegerType::Fixed(size, is_signed) => { - format!("{}{}", if is_signed { 'i' } else { 'u' }, size.size().bytes() * 8) - } - }; - out.push(&int_s); - } - if !out.is_empty() { - attrs.push(format!("#[repr({})]", out.join(", "))); - } + if let Some(repr_attr) = self.repr(tcx, cache) { + attrs.push(repr_attr); } attrs } + /// Returns a `#[repr(...)]` representation. + pub(crate) fn repr(&self, tcx: TyCtxt<'_>, cache: &Cache, is_json: bool) -> Option { + use rustc_abi::IntegerType; + + let def_id = self.def_id()?; + if !matches!(self.type_(), ItemType::Struct | ItemType::Enum | ItemType::Union) { + return None; + } + let adt = tcx.adt_def(def_id); + let repr = adt.repr(); + let mut out = Vec::new(); + if repr.c() { + out.push("C"); + } + if repr.transparent() { + // Render `repr(transparent)` iff the non-1-ZST field is public or at least one + // field is public in case all fields are 1-ZST fields. + let render_transparent = is_json + || cache.document_private + || adt + .all_fields() + .find(|field| { + let ty = field.ty(tcx, ty::GenericArgs::identity_for_item(tcx, field.did)); + tcx.layout_of( + ty::TypingEnv::post_analysis(tcx, field.did).as_query_input(ty), + ) + .is_ok_and(|layout| !layout.is_1zst()) + }) + .map_or_else( + || adt.all_fields().any(|field| field.vis.is_public()), + |field| field.vis.is_public(), + ); + + if render_transparent { + out.push("transparent"); + } + } + if repr.simd() { + out.push("simd"); + } + let pack_s; + if let Some(pack) = repr.pack { + pack_s = format!("packed({})", pack.bytes()); + out.push(&pack_s); + } + let align_s; + if let Some(align) = repr.align { + align_s = format!("align({})", align.bytes()); + out.push(&align_s); + } + let int_s; + if let Some(int) = repr.int { + int_s = match int { + IntegerType::Pointer(is_signed) => { + format!("{}size", if is_signed { 'i' } else { 'u' }) + } + IntegerType::Fixed(size, is_signed) => { + format!("{}{}", if is_signed { 'i' } else { 'u' }, size.size().bytes() * 8) + } + }; + out.push(&int_s); + } + if !out.is_empty() { Some(format!("#[repr({})]", out.join(", "))) } else { None } + } + pub fn is_doc_hidden(&self) -> bool { self.attrs.is_doc_hidden() } diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 06cb9269cc87f..6b4fd3cd834a9 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -1194,7 +1194,7 @@ fn render_assoc_item( // a whitespace prefix and newline. fn render_attributes_in_pre(it: &clean::Item, prefix: &str, cx: &Context<'_>) -> impl fmt::Display { fmt::from_fn(move |f| { - for a in it.attributes(cx.tcx(), cx.cache(), false) { + for a in it.attributes_and_repr(cx.tcx(), cx.cache(), false) { writeln!(f, "{prefix}{a}")?; } Ok(()) @@ -1204,7 +1204,7 @@ fn render_attributes_in_pre(it: &clean::Item, prefix: &str, cx: &Context<'_>) -> // When an attribute is rendered inside a tag, it is formatted using // a div to produce a newline after it. fn render_attributes_in_code(w: &mut impl fmt::Write, it: &clean::Item, cx: &Context<'_>) { - for attr in it.attributes(cx.tcx(), cx.cache(), false) { + for attr in it.attributes_and_repr(cx.tcx(), cx.cache(), false) { write!(w, "
{attr}
").unwrap(); } } diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index 705f9b2202c60..bfcb794b89a27 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -40,7 +40,7 @@ impl JsonRenderer<'_> { }) .collect(); let docs = item.opt_doc_value(); - let attrs = item.attributes(self.tcx, self.cache(), true); + let attrs = item.attributes_and_repr(self.tcx, self.cache(), true); let span = item.span(self.tcx); let visibility = item.visibility(self.tcx); let clean::ItemInner { name, item_id, .. } = *item.inner; From eb9f05481be266598527ba9b5eb890cc56555733 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 9 May 2025 17:44:24 +0200 Subject: [PATCH 13/18] Rename the `document_*` argument/field into `is_type_alias` --- src/librustdoc/html/render/print_item.rs | 22 +++++++++---------- src/librustdoc/html/templates/item_union.html | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index 616d89bca6a73..692e11b2689e3 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -1287,15 +1287,15 @@ fn item_type_alias(cx: &Context<'_>, it: &clean::Item, t: &clean::TypeAlias) -> is_non_exhaustive: *is_non_exhaustive, def_id: enum_def_id, } - .render_into(cx, it, false, w)?; + .render_into(cx, it, true, w)?; } clean::TypeAliasInnerType::Union { fields } => { - ItemUnion { cx, it, fields, generics: &t.generics, document_union: false } + ItemUnion { cx, it, fields, generics: &t.generics, is_type_alias: true } .render_into(w)?; } clean::TypeAliasInnerType::Struct { ctor_kind, fields } => { DisplayStruct { ctor_kind: *ctor_kind, generics: &t.generics, fields } - .render_into(cx, it, false, w)?; + .render_into(cx, it, true, w)?; } } } else { @@ -1416,7 +1416,7 @@ item_template!( it: &'a clean::Item, fields: &'a [clean::Item], generics: &'a clean::Generics, - document_union: bool, + is_type_alias: bool, }, methods = [document, document_type_layout, render_attributes_in_pre, render_assoc_items] ); @@ -1453,7 +1453,7 @@ impl<'a, 'cx: 'a> ItemUnion<'a, 'cx> { fn item_union(cx: &Context<'_>, it: &clean::Item, s: &clean::Union) -> impl fmt::Display { fmt::from_fn(|w| { - ItemUnion { cx, it, fields: &s.fields, generics: &s.generics, document_union: true } + ItemUnion { cx, it, fields: &s.fields, generics: &s.generics, is_type_alias: false } .render_into(w) .unwrap(); Ok(()) @@ -1494,7 +1494,7 @@ impl<'a> DisplayEnum<'a> { self, cx: &Context<'_>, it: &clean::Item, - document_enum: bool, + is_type_alias: bool, w: &mut W, ) -> fmt::Result { let variants_count = self.variants.iter().filter(|i| !i.is_stripped()).count(); @@ -1521,7 +1521,7 @@ impl<'a> DisplayEnum<'a> { ) })?; - if document_enum { + if !is_type_alias { write!(w, "{}", document(cx, it, None, HeadingOffset::H2))?; } @@ -1546,7 +1546,7 @@ fn item_enum(cx: &Context<'_>, it: &clean::Item, e: &clean::Enum) -> impl fmt::D is_non_exhaustive: it.is_non_exhaustive(), def_id: it.def_id().unwrap(), } - .render_into(cx, it, true, w) + .render_into(cx, it, false, w) }) } @@ -1945,7 +1945,7 @@ impl<'a> DisplayStruct<'a> { self, cx: &Context<'_>, it: &clean::Item, - document_struct: bool, + is_type_alias: bool, w: &mut W, ) -> fmt::Result { wrap_item(w, |w| { @@ -1957,7 +1957,7 @@ impl<'a> DisplayStruct<'a> { ) })?; - if document_struct { + if !is_type_alias { write!(w, "{}", document(cx, it, None, HeadingOffset::H2))?; } @@ -1975,7 +1975,7 @@ impl<'a> DisplayStruct<'a> { fn item_struct(cx: &Context<'_>, it: &clean::Item, s: &clean::Struct) -> impl fmt::Display { fmt::from_fn(|w| { DisplayStruct { ctor_kind: s.ctor_kind, generics: &s.generics, fields: s.fields.as_slice() } - .render_into(cx, it, true, w) + .render_into(cx, it, false, w) }) } diff --git a/src/librustdoc/html/templates/item_union.html b/src/librustdoc/html/templates/item_union.html index f3780fb4c5baa..99a9bc874dd59 100644 --- a/src/librustdoc/html/templates/item_union.html +++ b/src/librustdoc/html/templates/item_union.html @@ -2,7 +2,7 @@ {{ self.render_attributes_in_pre()|safe }} {{ self.render_union()|safe }}
-{% if self.document_union %} +{% if !self.is_type_alias %} {{ self.document()|safe }} {% endif %} {% if self.fields_iter().peek().is_some() %} From 4f3dd7b0180b1212aab607202ec6aa0d4fabb323 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 9 May 2025 18:47:25 +0200 Subject: [PATCH 14/18] Tweak attribute rendering depending on wether or not it is a type alias --- src/librustdoc/clean/types.rs | 129 ++++++++++++----------- src/librustdoc/html/render/mod.rs | 22 +++- src/librustdoc/html/render/print_item.rs | 100 +++++++++++++++--- tests/rustdoc/type-layout.rs | 2 +- 4 files changed, 173 insertions(+), 80 deletions(-) diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 1766627ce392c..c0bae5971a42e 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -815,67 +815,7 @@ impl Item { /// Returns a `#[repr(...)]` representation. pub(crate) fn repr(&self, tcx: TyCtxt<'_>, cache: &Cache, is_json: bool) -> Option { - use rustc_abi::IntegerType; - - let def_id = self.def_id()?; - if !matches!(self.type_(), ItemType::Struct | ItemType::Enum | ItemType::Union) { - return None; - } - let adt = tcx.adt_def(def_id); - let repr = adt.repr(); - let mut out = Vec::new(); - if repr.c() { - out.push("C"); - } - if repr.transparent() { - // Render `repr(transparent)` iff the non-1-ZST field is public or at least one - // field is public in case all fields are 1-ZST fields. - let render_transparent = is_json - || cache.document_private - || adt - .all_fields() - .find(|field| { - let ty = field.ty(tcx, ty::GenericArgs::identity_for_item(tcx, field.did)); - tcx.layout_of( - ty::TypingEnv::post_analysis(tcx, field.did).as_query_input(ty), - ) - .is_ok_and(|layout| !layout.is_1zst()) - }) - .map_or_else( - || adt.all_fields().any(|field| field.vis.is_public()), - |field| field.vis.is_public(), - ); - - if render_transparent { - out.push("transparent"); - } - } - if repr.simd() { - out.push("simd"); - } - let pack_s; - if let Some(pack) = repr.pack { - pack_s = format!("packed({})", pack.bytes()); - out.push(&pack_s); - } - let align_s; - if let Some(align) = repr.align { - align_s = format!("align({})", align.bytes()); - out.push(&align_s); - } - let int_s; - if let Some(int) = repr.int { - int_s = match int { - IntegerType::Pointer(is_signed) => { - format!("{}size", if is_signed { 'i' } else { 'u' }) - } - IntegerType::Fixed(size, is_signed) => { - format!("{}{}", if is_signed { 'i' } else { 'u' }, size.size().bytes() * 8) - } - }; - out.push(&int_s); - } - if !out.is_empty() { Some(format!("#[repr({})]", out.join(", "))) } else { None } + repr_attributes(tcx, cache, self.def_id()?, self.type_(), is_json) } pub fn is_doc_hidden(&self) -> bool { @@ -887,6 +827,73 @@ impl Item { } } +pub(crate) fn repr_attributes( + tcx: TyCtxt<'_>, + cache: &Cache, + def_id: DefId, + item_type: ItemType, + is_json: bool, +) -> Option { + use rustc_abi::IntegerType; + + if !matches!(item_type, ItemType::Struct | ItemType::Enum | ItemType::Union) { + return None; + } + let adt = tcx.adt_def(def_id); + let repr = adt.repr(); + let mut out = Vec::new(); + if repr.c() { + out.push("C"); + } + if repr.transparent() { + // Render `repr(transparent)` iff the non-1-ZST field is public or at least one + // field is public in case all fields are 1-ZST fields. + let render_transparent = cache.document_private + || is_json + || adt + .all_fields() + .find(|field| { + let ty = field.ty(tcx, ty::GenericArgs::identity_for_item(tcx, field.did)); + tcx.layout_of(ty::TypingEnv::post_analysis(tcx, field.did).as_query_input(ty)) + .is_ok_and(|layout| !layout.is_1zst()) + }) + .map_or_else( + || adt.all_fields().any(|field| field.vis.is_public()), + |field| field.vis.is_public(), + ); + + if render_transparent { + out.push("transparent"); + } + } + if repr.simd() { + out.push("simd"); + } + let pack_s; + if let Some(pack) = repr.pack { + pack_s = format!("packed({})", pack.bytes()); + out.push(&pack_s); + } + let align_s; + if let Some(align) = repr.align { + align_s = format!("align({})", align.bytes()); + out.push(&align_s); + } + let int_s; + if let Some(int) = repr.int { + int_s = match int { + IntegerType::Pointer(is_signed) => { + format!("{}size", if is_signed { 'i' } else { 'u' }) + } + IntegerType::Fixed(size, is_signed) => { + format!("{}{}", if is_signed { 'i' } else { 'u' }, size.size().bytes() * 8) + } + }; + out.push(&int_s); + } + if !out.is_empty() { Some(format!("#[repr({})]", out.join(", "))) } else { None } +} + #[derive(Clone, Debug)] pub(crate) enum ItemKind { ExternCrateItem { diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 6b4fd3cd834a9..f155ea52040a1 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -1201,11 +1201,31 @@ fn render_attributes_in_pre(it: &clean::Item, prefix: &str, cx: &Context<'_>) -> }) } +struct CodeAttribute(String); + +impl CodeAttribute { + fn render_into(self, w: &mut impl fmt::Write) { + write!(w, "
{}
", self.0).unwrap(); + } +} + // When an attribute is rendered inside a tag, it is formatted using // a div to produce a newline after it. fn render_attributes_in_code(w: &mut impl fmt::Write, it: &clean::Item, cx: &Context<'_>) { for attr in it.attributes_and_repr(cx.tcx(), cx.cache(), false) { - write!(w, "
{attr}
").unwrap(); + CodeAttribute(attr).render_into(w); + } +} + +/// used for type aliases to only render their `repr` attribute. +fn render_repr_attributes_in_code( + w: &mut impl fmt::Write, + cx: &Context<'_>, + def_id: DefId, + item_type: ItemType, +) { + if let Some(repr) = clean::repr_attributes(cx.tcx(), cx.cache(), def_id, item_type) { + CodeAttribute(repr).render_into(w); } } diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index 692e11b2689e3..5f81ec68548ff 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -20,7 +20,7 @@ use super::{ collect_paths_for_type, document, ensure_trailing_slash, get_filtered_impls_for_reference, item_ty_to_section, notable_traits_button, notable_traits_json, render_all_impls, render_assoc_item, render_assoc_items, render_attributes_in_code, render_attributes_in_pre, - render_impl, render_rightside, render_stability_since_raw, + render_impl, render_repr_attributes_in_code, render_rightside, render_stability_since_raw, render_stability_since_raw_with_extra, write_section_heading, }; use crate::clean; @@ -1290,12 +1290,30 @@ fn item_type_alias(cx: &Context<'_>, it: &clean::Item, t: &clean::TypeAlias) -> .render_into(cx, it, true, w)?; } clean::TypeAliasInnerType::Union { fields } => { - ItemUnion { cx, it, fields, generics: &t.generics, is_type_alias: true } - .render_into(w)?; + let ty = cx.tcx().type_of(it.def_id().unwrap()).instantiate_identity(); + let union_def_id = ty.ty_adt_def().unwrap().did(); + + ItemUnion { + cx, + it, + fields, + generics: &t.generics, + is_type_alias: true, + def_id: union_def_id, + } + .render_into(w)?; } clean::TypeAliasInnerType::Struct { ctor_kind, fields } => { - DisplayStruct { ctor_kind: *ctor_kind, generics: &t.generics, fields } - .render_into(cx, it, true, w)?; + let ty = cx.tcx().type_of(it.def_id().unwrap()).instantiate_identity(); + let struct_def_id = ty.ty_adt_def().unwrap().did(); + + DisplayStruct { + ctor_kind: *ctor_kind, + generics: &t.generics, + fields, + def_id: struct_def_id, + } + .render_into(cx, it, true, w)?; } } } else { @@ -1417,8 +1435,9 @@ item_template!( fields: &'a [clean::Item], generics: &'a clean::Generics, is_type_alias: bool, + def_id: DefId, }, - methods = [document, document_type_layout, render_attributes_in_pre, render_assoc_items] + methods = [document, document_type_layout, render_assoc_items] ); impl<'a, 'cx: 'a> ItemUnion<'a, 'cx> { @@ -1449,13 +1468,41 @@ impl<'a, 'cx: 'a> ItemUnion<'a, 'cx> { }) .peekable() } + + fn render_attributes_in_pre(&self) -> impl fmt::Display { + fmt::from_fn(move |f| { + if !self.is_type_alias { + for a in self.it.attributes_and_repr(self.cx.tcx(), self.cx.cache(), false) { + writeln!(f, "{a}")?; + } + } else { + // For now we only render `repr` attributes for type aliases. + if let Some(repr) = clean::repr_attributes( + self.cx.tcx(), + self.cx.cache(), + self.def_id, + ItemType::Union, + ) { + writeln!(f, "{repr}")?; + }; + } + Ok(()) + }) + } } fn item_union(cx: &Context<'_>, it: &clean::Item, s: &clean::Union) -> impl fmt::Display { fmt::from_fn(|w| { - ItemUnion { cx, it, fields: &s.fields, generics: &s.generics, is_type_alias: false } - .render_into(w) - .unwrap(); + ItemUnion { + cx, + it, + fields: &s.fields, + generics: &s.generics, + is_type_alias: false, + def_id: it.def_id().unwrap(), + } + .render_into(w) + .unwrap(); Ok(()) }) } @@ -1502,7 +1549,12 @@ impl<'a> DisplayEnum<'a> { let has_stripped_entries = variants_len != variants_count; wrap_item(w, |w| { - render_attributes_in_code(w, it, cx); + if !is_type_alias { + render_attributes_in_code(w, it, cx); + } else { + // For now we only render `repr` attributes for type aliases. + render_repr_attributes_in_code(w, cx, self.def_id, ItemType::Enum); + } write!( w, "{}enum {}{}{}", @@ -1521,19 +1573,22 @@ impl<'a> DisplayEnum<'a> { ) })?; - if !is_type_alias { + let def_id = it.item_id.expect_def_id(); + let layout_def_id = if !is_type_alias { write!(w, "{}", document(cx, it, None, HeadingOffset::H2))?; - } + def_id + } else { + self.def_id + }; if variants_count != 0 { write!(w, "{}", item_variants(cx, it, self.variants, self.def_id))?; } - let def_id = it.item_id.expect_def_id(); write!( w, "{}{}", render_assoc_items(cx, it, def_id, AssocItemRender::All), - document_type_layout(cx, def_id) + document_type_layout(cx, layout_def_id) ) } } @@ -1938,6 +1993,7 @@ struct DisplayStruct<'a> { ctor_kind: Option, generics: &'a clean::Generics, fields: &'a [clean::Item], + def_id: DefId, } impl<'a> DisplayStruct<'a> { @@ -1949,7 +2005,12 @@ impl<'a> DisplayStruct<'a> { w: &mut W, ) -> fmt::Result { wrap_item(w, |w| { - render_attributes_in_code(w, it, cx); + if !is_type_alias { + render_attributes_in_code(w, it, cx); + } else { + // For now we only render `repr` attributes for type aliases. + render_repr_attributes_in_code(w, cx, self.def_id, ItemType::Struct); + } write!( w, "{}", @@ -1974,8 +2035,13 @@ impl<'a> DisplayStruct<'a> { fn item_struct(cx: &Context<'_>, it: &clean::Item, s: &clean::Struct) -> impl fmt::Display { fmt::from_fn(|w| { - DisplayStruct { ctor_kind: s.ctor_kind, generics: &s.generics, fields: s.fields.as_slice() } - .render_into(cx, it, false, w) + DisplayStruct { + ctor_kind: s.ctor_kind, + generics: &s.generics, + fields: s.fields.as_slice(), + def_id: it.def_id().unwrap(), + } + .render_into(cx, it, false, w) }) } diff --git a/tests/rustdoc/type-layout.rs b/tests/rustdoc/type-layout.rs index 6de435dbcc143..482b8b597dd30 100644 --- a/tests/rustdoc/type-layout.rs +++ b/tests/rustdoc/type-layout.rs @@ -61,7 +61,7 @@ pub type TypeAlias = X; pub type GenericTypeAlias = (Generic<(u32, ())>, Generic); // Regression test for the rustdoc equivalent of #85103. -//@ hasraw type_layout/type.Edges.html 'Encountered an error during type layout; the type failed to be normalized.' +//@ hasraw type_layout/type.Edges.html 'Unable to compute type layout, possibly due to this type having generic parameters. Layout can only be computed for concrete, fully-instantiated types.' pub type Edges<'a, E> = std::borrow::Cow<'a, [E]>; //@ !hasraw type_layout/trait.MyTrait.html 'Size: ' From 2b292d1b78194e0fb702b1271f4322d0b1d5d1bf Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 9 May 2025 18:47:50 +0200 Subject: [PATCH 15/18] Add regression test for #140739 --- tests/rustdoc/type-alias/repr.rs | 42 ++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 tests/rustdoc/type-alias/repr.rs diff --git a/tests/rustdoc/type-alias/repr.rs b/tests/rustdoc/type-alias/repr.rs new file mode 100644 index 0000000000000..cf90798036099 --- /dev/null +++ b/tests/rustdoc/type-alias/repr.rs @@ -0,0 +1,42 @@ +// This test ensures that the `repr` attribute is displayed in type aliases. +// +// Regression test for . + +#![crate_name = "foo"] + +/// bla +#[repr(C)] +pub struct Foo1; + +//@ has 'foo/type.Bar1.html' +//@ has - '//*[@class="rust item-decl"]/code' '#[repr(C)]pub struct Bar1;' +// Ensures that we see the doc comment of the type alias and not of the aliased type. +//@ has - '//*[@class="toggle top-doc"]/*[@class="docblock"]' 'bar' +/// bar +pub type Bar1 = Foo1; + +/// bla +#[repr(C)] +pub union Foo2 { + pub a: u8, +} + +//@ has 'foo/type.Bar2.html' +//@ matches - '//*[@class="rust item-decl"]' '#\[repr\(C\)\]\npub union Bar2 \{*' +// Ensures that we see the doc comment of the type alias and not of the aliased type. +//@ has - '//*[@class="toggle top-doc"]/*[@class="docblock"]' 'bar' +/// bar +pub type Bar2 = Foo2; + +/// bla +#[repr(C)] +pub enum Foo3 { + A, +} + +//@ has 'foo/type.Bar3.html' +//@ matches - '//*[@class="rust item-decl"]' '#\[repr\(C\)\]pub enum Bar3 \{*' +// Ensures that we see the doc comment of the type alias and not of the aliased type. +//@ has - '//*[@class="toggle top-doc"]/*[@class="docblock"]' 'bar' +/// bar +pub type Bar3 = Foo3; From 3646a09811bfca55e976117b4c2b8bf0b4cb5aee Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 21 May 2025 22:55:50 +0200 Subject: [PATCH 16/18] Improve code --- src/librustdoc/clean/types.rs | 11 +-- src/librustdoc/html/render/mod.rs | 10 +-- src/librustdoc/html/render/print_item.rs | 76 ++++++++++--------- src/librustdoc/html/templates/item_union.html | 2 +- 4 files changed, 50 insertions(+), 49 deletions(-) diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index c0bae5971a42e..63533f9fb83a4 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -764,7 +764,7 @@ impl Item { Some(tcx.visibility(def_id)) } - pub(crate) fn attributes_witout_repr(&self, tcx: TyCtxt<'_>, is_json: bool) -> Vec { + pub(crate) fn attributes_without_repr(&self, tcx: TyCtxt<'_>, is_json: bool) -> Vec { const ALLOWED_ATTRIBUTES: &[Symbol] = &[sym::export_name, sym::link_section, sym::no_mangle, sym::non_exhaustive]; @@ -805,7 +805,7 @@ impl Item { cache: &Cache, is_json: bool, ) -> Vec { - let mut attrs = self.attributes_witout_repr(tcx, is_json); + let mut attrs = self.attributes_without_repr(tcx, is_json); if let Some(repr_attr) = self.repr(tcx, cache) { attrs.push(repr_attr); @@ -813,7 +813,7 @@ impl Item { attrs } - /// Returns a `#[repr(...)]` representation. + /// Returns a stringified `#[repr(...)]` attribute. pub(crate) fn repr(&self, tcx: TyCtxt<'_>, cache: &Cache, is_json: bool) -> Option { repr_attributes(tcx, cache, self.def_id()?, self.type_(), is_json) } @@ -2370,8 +2370,9 @@ impl TypeAliasInnerType { fn has_stripped_entries(&self) -> Option { Some(match self { Self::Enum { variants, .. } => variants.iter().any(|v| v.is_stripped()), - Self::Union { fields } => fields.iter().any(|f| f.is_stripped()), - Self::Struct { fields, .. } => fields.iter().any(|f| f.is_stripped()), + Self::Union { fields } | Self::Struct { fields, .. } => { + fields.iter().any(|f| f.is_stripped()) + } }) } } diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index f155ea52040a1..14b35d3c39208 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -1203,17 +1203,15 @@ fn render_attributes_in_pre(it: &clean::Item, prefix: &str, cx: &Context<'_>) -> struct CodeAttribute(String); -impl CodeAttribute { - fn render_into(self, w: &mut impl fmt::Write) { - write!(w, "
{}
", self.0).unwrap(); - } +fn render_code_attribute(code_attr: CodeAttribute, w: &mut impl fmt::Write) { + write!(w, "
{}
", code_attr.0).unwrap(); } // When an attribute is rendered inside a tag, it is formatted using // a div to produce a newline after it. fn render_attributes_in_code(w: &mut impl fmt::Write, it: &clean::Item, cx: &Context<'_>) { for attr in it.attributes_and_repr(cx.tcx(), cx.cache(), false) { - CodeAttribute(attr).render_into(w); + render_code_attribute(CodeAttribute(attr), w); } } @@ -1225,7 +1223,7 @@ fn render_repr_attributes_in_code( item_type: ItemType, ) { if let Some(repr) = clean::repr_attributes(cx.tcx(), cx.cache(), def_id, item_type) { - CodeAttribute(repr).render_into(w); + render_code_attribute(CodeAttribute(repr), w); } } diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index 5f81ec68548ff..3ef578cb03d0e 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -1457,26 +1457,23 @@ impl<'a, 'cx: 'a> ItemUnion<'a, 'cx> { ty.print(self.cx) } - fn fields_iter( - &self, - ) -> iter::Peekable> { - self.fields - .iter() - .filter_map(|f| match f.kind { - clean::StructFieldItem(ref ty) => Some((f, ty)), - _ => None, - }) - .peekable() + // FIXME (GuillaumeGomez): When is implemented, + // we can replace the returned value with: + // + // `iter::Peekable>` + // + // And update `item_union.html`. + fn fields_iter(&self) -> impl Iterator { + self.fields.iter().filter_map(|f| match f.kind { + clean::StructFieldItem(ref ty) => Some((f, ty)), + _ => None, + }) } fn render_attributes_in_pre(&self) -> impl fmt::Display { fmt::from_fn(move |f| { - if !self.is_type_alias { - for a in self.it.attributes_and_repr(self.cx.tcx(), self.cx.cache(), false) { - writeln!(f, "{a}")?; - } - } else { - // For now we only render `repr` attributes for type aliases. + if self.is_type_alias { + // For now the only attributes we render for type aliases are `repr` attributes. if let Some(repr) = clean::repr_attributes( self.cx.tcx(), self.cx.cache(), @@ -1485,6 +1482,10 @@ impl<'a, 'cx: 'a> ItemUnion<'a, 'cx> { ) { writeln!(f, "{repr}")?; }; + } else { + for a in self.it.attributes_and_repr(self.cx.tcx(), self.cx.cache(), false) { + writeln!(f, "{a}")?; + } } Ok(()) }) @@ -1501,8 +1502,7 @@ fn item_union(cx: &Context<'_>, it: &clean::Item, s: &clean::Union) -> impl fmt: is_type_alias: false, def_id: it.def_id().unwrap(), } - .render_into(w) - .unwrap(); + .render_into(w)?; Ok(()) }) } @@ -1529,14 +1529,14 @@ fn print_tuple_struct_fields(cx: &Context<'_>, s: &[clean::Item]) -> impl Displa }) } -struct DisplayEnum<'a> { - variants: &'a IndexVec, - generics: &'a clean::Generics, +struct DisplayEnum<'clean> { + variants: &'clean IndexVec, + generics: &'clean clean::Generics, is_non_exhaustive: bool, def_id: DefId, } -impl<'a> DisplayEnum<'a> { +impl<'clean> DisplayEnum<'clean> { fn render_into( self, cx: &Context<'_>, @@ -1544,16 +1544,16 @@ impl<'a> DisplayEnum<'a> { is_type_alias: bool, w: &mut W, ) -> fmt::Result { - let variants_count = self.variants.iter().filter(|i| !i.is_stripped()).count(); + let non_stripped_variant_count = self.variants.iter().filter(|i| !i.is_stripped()).count(); let variants_len = self.variants.len(); - let has_stripped_entries = variants_len != variants_count; + let has_stripped_entries = variants_len != non_stripped_variant_count; wrap_item(w, |w| { - if !is_type_alias { - render_attributes_in_code(w, it, cx); - } else { - // For now we only render `repr` attributes for type aliases. + if is_type_alias { + // For now the only attributes we render for type aliases are `repr` attributes. render_repr_attributes_in_code(w, cx, self.def_id, ItemType::Enum); + } else { + render_attributes_in_code(w, it, cx); } write!( w, @@ -1565,7 +1565,7 @@ impl<'a> DisplayEnum<'a> { cx, Some(self.generics), self.variants, - variants_count, + non_stripped_variant_count, has_stripped_entries, self.is_non_exhaustive, self.def_id, @@ -1574,14 +1574,16 @@ impl<'a> DisplayEnum<'a> { })?; let def_id = it.item_id.expect_def_id(); - let layout_def_id = if !is_type_alias { + let layout_def_id = if is_type_alias { + self.def_id + } else { write!(w, "{}", document(cx, it, None, HeadingOffset::H2))?; + // We don't return the same `DefId` since the layout size of the type alias might be + // different since we might have more information on the generics. def_id - } else { - self.def_id }; - if variants_count != 0 { + if non_stripped_variant_count != 0 { write!(w, "{}", item_variants(cx, it, self.variants, self.def_id))?; } write!( @@ -2005,11 +2007,11 @@ impl<'a> DisplayStruct<'a> { w: &mut W, ) -> fmt::Result { wrap_item(w, |w| { - if !is_type_alias { - render_attributes_in_code(w, it, cx); - } else { - // For now we only render `repr` attributes for type aliases. + if is_type_alias { + // For now the only attributes we render for type aliases are `repr` attributes. render_repr_attributes_in_code(w, cx, self.def_id, ItemType::Struct); + } else { + render_attributes_in_code(w, it, cx); } write!( w, diff --git a/src/librustdoc/html/templates/item_union.html b/src/librustdoc/html/templates/item_union.html index 99a9bc874dd59..b5d3367a6a10b 100644 --- a/src/librustdoc/html/templates/item_union.html +++ b/src/librustdoc/html/templates/item_union.html @@ -5,7 +5,7 @@ {% if !self.is_type_alias %} {{ self.document()|safe }} {% endif %} -{% if self.fields_iter().peek().is_some() %} +{% if self.fields_iter().next().is_some() %}

{# #} Fields§ {# #}

From ec97b0f0b57376550efa4d144b62955970f22f02 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sun, 25 May 2025 15:27:32 +0200 Subject: [PATCH 17/18] Update to new API --- src/librustdoc/clean/types.rs | 2 +- src/librustdoc/html/render/mod.rs | 2 +- src/librustdoc/html/render/print_item.rs | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 63533f9fb83a4..bb3469867d51e 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -807,7 +807,7 @@ impl Item { ) -> Vec { let mut attrs = self.attributes_without_repr(tcx, is_json); - if let Some(repr_attr) = self.repr(tcx, cache) { + if let Some(repr_attr) = self.repr(tcx, cache, is_json) { attrs.push(repr_attr); } attrs diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 14b35d3c39208..5677b13033db5 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -1222,7 +1222,7 @@ fn render_repr_attributes_in_code( def_id: DefId, item_type: ItemType, ) { - if let Some(repr) = clean::repr_attributes(cx.tcx(), cx.cache(), def_id, item_type) { + if let Some(repr) = clean::repr_attributes(cx.tcx(), cx.cache(), def_id, item_type, false) { render_code_attribute(CodeAttribute(repr), w); } } diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index 3ef578cb03d0e..b4663961c1b71 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -1479,6 +1479,7 @@ impl<'a, 'cx: 'a> ItemUnion<'a, 'cx> { self.cx.cache(), self.def_id, ItemType::Union, + false, ) { writeln!(f, "{repr}")?; }; From cf9ac0eec16bb863add283b58de5a25a131b142d Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 25 May 2025 22:16:10 +0200 Subject: [PATCH 18/18] const-check: stop recommending the use of rustc_allow_const_fn_unstable --- compiler/rustc_const_eval/messages.ftl | 3 +- compiler/rustc_const_eval/src/errors.rs | 5 - .../dont_promote_unstable_const_fn.stderr | 7 +- .../ui/consts/const-unstable-intrinsic.stderr | 21 +-- .../min_const_fn_libstd_stability.stderr | 49 +------ ...in_const_unsafe_fn_libstd_stability.stderr | 21 +-- ...n_const_unsafe_fn_libstd_stability2.stderr | 21 +-- ...e_const_stab_unmarked_crate_imports.stderr | 7 +- ...ive_const_stab_unstable_if_unmarked.stderr | 14 +- .../const-eval-select-stability.stderr | 7 +- .../ui/traits/const-traits/staged-api.stderr | 133 +++--------------- 11 files changed, 41 insertions(+), 247 deletions(-) diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl index f4defd2aa1343..7d4afc9d3d952 100644 --- a/compiler/rustc_const_eval/messages.ftl +++ b/compiler/rustc_const_eval/messages.ftl @@ -424,8 +424,7 @@ const_eval_unstable_in_stable_exposed = .unstable_sugg = if the {$is_function_call2 -> [true] caller *[false] function - } is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) - .bypass_sugg = otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) + } is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` const_eval_unstable_intrinsic = `{$name}` is not yet stable as a const intrinsic const_eval_unstable_intrinsic_suggestion = add `#![feature({$feature})]` to the crate attributes to enable diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs index 826ea0e58ecca..7c35e47bbf805 100644 --- a/compiler/rustc_const_eval/src/errors.rs +++ b/compiler/rustc_const_eval/src/errors.rs @@ -58,11 +58,6 @@ pub(crate) struct UnstableInStableExposed { code = "#[rustc_const_unstable(feature = \"...\", issue = \"...\")]\n", applicability = "has-placeholders" )] - #[suggestion( - const_eval_bypass_sugg, - code = "#[rustc_allow_const_fn_unstable({gate})]\n", - applicability = "has-placeholders" - )] pub attr_span: Span, } diff --git a/tests/ui/consts/const-eval/dont_promote_unstable_const_fn.stderr b/tests/ui/consts/const-eval/dont_promote_unstable_const_fn.stderr index a506f2a282bbb..b505b76a6abfa 100644 --- a/tests/ui/consts/const-eval/dont_promote_unstable_const_fn.stderr +++ b/tests/ui/consts/const-eval/dont_promote_unstable_const_fn.stderr @@ -5,14 +5,9 @@ LL | const fn bar() -> u32 { foo() } | ^^^^^ | = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unstable features -help: if the caller is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the caller is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | const fn bar() -> u32 { foo() } - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(foo)] LL | const fn bar() -> u32 { foo() } | diff --git a/tests/ui/consts/const-unstable-intrinsic.stderr b/tests/ui/consts/const-unstable-intrinsic.stderr index 308b02386f5c9..7e7ba966cee17 100644 --- a/tests/ui/consts/const-unstable-intrinsic.stderr +++ b/tests/ui/consts/const-unstable-intrinsic.stderr @@ -46,14 +46,9 @@ error: const function that might be (indirectly) exposed to stable cannot use `# LL | size_of_val(&x); | ^^^^^^^^^^^^^^^ | -help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the function is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | const fn const_main() { - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(local)] LL | const fn const_main() { | @@ -63,14 +58,9 @@ error: const function that might be (indirectly) exposed to stable cannot use `# LL | min_align_of_val(&x); | ^^^^^^^^^^^^^^^^^^^^ | -help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the function is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | const fn const_main() { - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(local)] LL | const fn const_main() { | @@ -88,14 +78,9 @@ error: const function that might be (indirectly) exposed to stable cannot use `# LL | super::size_of_val(src); | ^^^^^^^^^^^^^^^^^^^^^^^ | -help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the function is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | const unsafe fn copy(src: *const T, _dst: *mut T, _count: usize) { - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(local)] LL | const unsafe fn copy(src: *const T, _dst: *mut T, _count: usize) { | diff --git a/tests/ui/consts/min_const_fn/min_const_fn_libstd_stability.stderr b/tests/ui/consts/min_const_fn/min_const_fn_libstd_stability.stderr index 26dedc49a3928..9efc252ce6b09 100644 --- a/tests/ui/consts/min_const_fn/min_const_fn_libstd_stability.stderr +++ b/tests/ui/consts/min_const_fn/min_const_fn_libstd_stability.stderr @@ -5,14 +5,9 @@ LL | const fn bar() -> u32 { foo() } | ^^^^^ | = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unstable features -help: if the caller is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the caller is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | const fn bar() -> u32 { foo() } - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(foo)] LL | const fn bar() -> u32 { foo() } | @@ -23,14 +18,9 @@ LL | const fn bar2() -> u32 { foo2() } | ^^^^^^ | = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unstable features -help: if the caller is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the caller is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | const fn bar2() -> u32 { foo2() } - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(foo2)] LL | const fn bar2() -> u32 { foo2() } | @@ -40,14 +30,9 @@ error: const function that might be (indirectly) exposed to stable cannot use `# LL | let x = async { 13 }; | ^^^^^^^^^^^^ | -help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the function is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | const fn bar3() -> u32 { - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(const_async_blocks)] LL | const fn bar3() -> u32 { | @@ -58,14 +43,9 @@ LL | foo() | ^^^^^ | = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unstable features -help: if the caller is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the caller is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | const fn bar3() -> u32 { - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(foo)] LL | const fn bar3() -> u32 { | @@ -76,14 +56,9 @@ LL | const fn bar2_gated() -> u32 { foo2_gated() } | ^^^^^^^^^^^^ | = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unstable features -help: if the caller is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the caller is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | const fn bar2_gated() -> u32 { foo2_gated() } - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(foo2)] LL | const fn bar2_gated() -> u32 { foo2_gated() } | @@ -94,14 +69,9 @@ LL | pub(crate) const fn bar2_gated_stable_indirect() -> u32 { super::foo2_g | ^^^^^^^^^^^^^^^^^^^ | = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unstable features -help: if the caller is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the caller is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | pub(crate) const fn bar2_gated_stable_indirect() -> u32 { super::foo2_gated() } - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(foo2)] LL | pub(crate) const fn bar2_gated_stable_indirect() -> u32 { super::foo2_gated() } | @@ -112,14 +82,9 @@ LL | const fn stable_indirect() -> u32 { foo2_gated() } | ^^^^^^^^^^^^ | = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unstable features -help: if the caller is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the caller is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | const fn stable_indirect() -> u32 { foo2_gated() } - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(foo2)] LL | const fn stable_indirect() -> u32 { foo2_gated() } | diff --git a/tests/ui/consts/min_const_fn/min_const_unsafe_fn_libstd_stability.stderr b/tests/ui/consts/min_const_fn/min_const_unsafe_fn_libstd_stability.stderr index b61f7db6f43b7..0712a790955a8 100644 --- a/tests/ui/consts/min_const_fn/min_const_unsafe_fn_libstd_stability.stderr +++ b/tests/ui/consts/min_const_fn/min_const_unsafe_fn_libstd_stability.stderr @@ -5,14 +5,9 @@ LL | const unsafe fn bar() -> u32 { unsafe { foo() } } | ^^^^^ | = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unstable features -help: if the caller is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the caller is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | const unsafe fn bar() -> u32 { unsafe { foo() } } - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(foo)] LL | const unsafe fn bar() -> u32 { unsafe { foo() } } | @@ -23,14 +18,9 @@ LL | const unsafe fn bar2() -> u32 { unsafe { foo2() } } | ^^^^^^ | = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unstable features -help: if the caller is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the caller is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | const unsafe fn bar2() -> u32 { unsafe { foo2() } } - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(foo2)] LL | const unsafe fn bar2() -> u32 { unsafe { foo2() } } | @@ -41,14 +31,9 @@ LL | const unsafe fn bar2_gated() -> u32 { unsafe { foo2_gated() } } | ^^^^^^^^^^^^ | = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unstable features -help: if the caller is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the caller is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | const unsafe fn bar2_gated() -> u32 { unsafe { foo2_gated() } } - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(foo2)] LL | const unsafe fn bar2_gated() -> u32 { unsafe { foo2_gated() } } | diff --git a/tests/ui/consts/min_const_fn/min_const_unsafe_fn_libstd_stability2.stderr b/tests/ui/consts/min_const_fn/min_const_unsafe_fn_libstd_stability2.stderr index fad8e396e9ab5..618b9a16dd468 100644 --- a/tests/ui/consts/min_const_fn/min_const_unsafe_fn_libstd_stability2.stderr +++ b/tests/ui/consts/min_const_fn/min_const_unsafe_fn_libstd_stability2.stderr @@ -5,14 +5,9 @@ LL | const unsafe fn bar() -> u32 { foo() } | ^^^^^ | = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unstable features -help: if the caller is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the caller is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | const unsafe fn bar() -> u32 { foo() } - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(foo)] LL | const unsafe fn bar() -> u32 { foo() } | @@ -23,14 +18,9 @@ LL | const unsafe fn bar2() -> u32 { foo2() } | ^^^^^^ | = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unstable features -help: if the caller is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the caller is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | const unsafe fn bar2() -> u32 { foo2() } - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(foo2)] LL | const unsafe fn bar2() -> u32 { foo2() } | @@ -41,14 +31,9 @@ LL | const unsafe fn bar2_gated() -> u32 { foo2_gated() } | ^^^^^^^^^^^^ | = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unstable features -help: if the caller is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the caller is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | const unsafe fn bar2_gated() -> u32 { foo2_gated() } - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(foo2)] LL | const unsafe fn bar2_gated() -> u32 { foo2_gated() } | diff --git a/tests/ui/consts/min_const_fn/recursive_const_stab_unmarked_crate_imports.stderr b/tests/ui/consts/min_const_fn/recursive_const_stab_unmarked_crate_imports.stderr index bbe749f595893..04804cb6d339b 100644 --- a/tests/ui/consts/min_const_fn/recursive_const_stab_unmarked_crate_imports.stderr +++ b/tests/ui/consts/min_const_fn/recursive_const_stab_unmarked_crate_imports.stderr @@ -5,14 +5,9 @@ LL | unstable_if_unmarked_const_fn_crate::not_stably_const(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unstable features -help: if the caller is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the caller is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | const fn stable_fn() { - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(rustc_private)] LL | const fn stable_fn() { | diff --git a/tests/ui/consts/min_const_fn/recursive_const_stab_unstable_if_unmarked.stderr b/tests/ui/consts/min_const_fn/recursive_const_stab_unstable_if_unmarked.stderr index 9d7b81c822bd1..14940ae93f894 100644 --- a/tests/ui/consts/min_const_fn/recursive_const_stab_unstable_if_unmarked.stderr +++ b/tests/ui/consts/min_const_fn/recursive_const_stab_unstable_if_unmarked.stderr @@ -5,14 +5,9 @@ LL | not_stably_const(); | ^^^^^^^^^^^^^^^^^^ | = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unstable features -help: if the caller is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the caller is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | pub const fn expose_on_stable() { - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(rustc_private)] LL | pub const fn expose_on_stable() { | @@ -22,14 +17,9 @@ error: const function that might be (indirectly) exposed to stable cannot use `# LL | let _x = async { 15 }; | ^^^^^^^^^^^^ | -help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the function is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | pub const fn expose_on_stable() { - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(const_async_blocks)] LL | pub const fn expose_on_stable() { | diff --git a/tests/ui/intrinsics/const-eval-select-stability.stderr b/tests/ui/intrinsics/const-eval-select-stability.stderr index 5f443b1d4ff7b..513c19cbb5b71 100644 --- a/tests/ui/intrinsics/const-eval-select-stability.stderr +++ b/tests/ui/intrinsics/const-eval-select-stability.stderr @@ -4,14 +4,9 @@ error: const function that might be (indirectly) exposed to stable cannot use `# LL | const_eval_select((), nothing, log); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the function is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | pub const fn hey() { - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(const_eval_select)] LL | pub const fn hey() { | diff --git a/tests/ui/traits/const-traits/staged-api.stderr b/tests/ui/traits/const-traits/staged-api.stderr index cdf577287eec5..4756c490cb10a 100644 --- a/tests/ui/traits/const-traits/staged-api.stderr +++ b/tests/ui/traits/const-traits/staged-api.stderr @@ -71,14 +71,9 @@ error: const function that might be (indirectly) exposed to stable cannot use `# LL | Unstable::func(); | ^^^^^^^^^^^^^^^^ | -help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the function is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | const fn const_context() { - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(const_trait_impl)] LL | const fn const_context() { | @@ -88,14 +83,9 @@ error: const function that might be (indirectly) exposed to stable cannot use `# LL | Unstable::func(); | ^^^^^^^^^^^^^^^^ | -help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the function is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | const fn const_context() { - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(unstable)] LL | const fn const_context() { | @@ -105,14 +95,9 @@ error: const function that might be (indirectly) exposed to stable cannot use `# LL | Foo::func(); | ^^^^^^^^^^^ | -help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the function is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | const fn const_context() { - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(const_trait_impl)] LL | const fn const_context() { | @@ -122,14 +107,9 @@ error: const function that might be (indirectly) exposed to stable cannot use `# LL | Foo::func(); | ^^^^^^^^^^^ | -help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the function is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | const fn const_context() { - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(unstable)] LL | const fn const_context() { | @@ -139,14 +119,9 @@ error: const function that might be (indirectly) exposed to stable cannot use `# LL | Unstable2::func(); | ^^^^^^^^^^^^^^^^^ | -help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the function is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | const fn const_context() { - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(const_trait_impl)] LL | const fn const_context() { | @@ -156,14 +131,9 @@ error: const function that might be (indirectly) exposed to stable cannot use `# LL | Unstable2::func(); | ^^^^^^^^^^^^^^^^^ | -help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the function is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | const fn const_context() { - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(unstable)] LL | const fn const_context() { | @@ -173,14 +143,9 @@ error: const function that might be (indirectly) exposed to stable cannot use `# LL | conditionally_const::(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the function is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | const fn const_context() { - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(const_trait_impl)] LL | const fn const_context() { | @@ -190,14 +155,9 @@ error: const function that might be (indirectly) exposed to stable cannot use `# LL | Unstable::func(); | ^^^^^^^^^^^^^^^^ | -help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the function is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | const fn stable_const_context() { - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(const_trait_impl)] LL | const fn stable_const_context() { | @@ -207,14 +167,9 @@ error: const function that might be (indirectly) exposed to stable cannot use `# LL | Unstable::func(); | ^^^^^^^^^^^^^^^^ | -help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the function is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | const fn stable_const_context() { - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(unstable)] LL | const fn stable_const_context() { | @@ -224,14 +179,9 @@ error: const function that might be (indirectly) exposed to stable cannot use `# LL | Foo::func(); | ^^^^^^^^^^^ | -help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the function is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | const fn stable_const_context() { - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(const_trait_impl)] LL | const fn stable_const_context() { | @@ -241,14 +191,9 @@ error: const function that might be (indirectly) exposed to stable cannot use `# LL | Foo::func(); | ^^^^^^^^^^^ | -help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the function is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | const fn stable_const_context() { - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(unstable)] LL | const fn stable_const_context() { | @@ -259,14 +204,9 @@ LL | const_context_not_const_stable(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unstable features -help: if the caller is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the caller is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | const fn stable_const_context() { - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(local_feature)] LL | const fn stable_const_context() { | @@ -276,14 +216,9 @@ error: const function that might be (indirectly) exposed to stable cannot use `# LL | conditionally_const::(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the function is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | const fn stable_const_context() { - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(const_trait_impl)] LL | const fn stable_const_context() { | @@ -293,14 +228,9 @@ error: const function that might be (indirectly) exposed to stable cannot use `# LL | Unstable::func(); | ^^^^^^^^^^^^^^^^ | -help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the function is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | const fn implicitly_stable_const_context() { - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(const_trait_impl)] LL | const fn implicitly_stable_const_context() { | @@ -310,14 +240,9 @@ error: const function that might be (indirectly) exposed to stable cannot use `# LL | Unstable::func(); | ^^^^^^^^^^^^^^^^ | -help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the function is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | const fn implicitly_stable_const_context() { - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(unstable)] LL | const fn implicitly_stable_const_context() { | @@ -327,14 +252,9 @@ error: const function that might be (indirectly) exposed to stable cannot use `# LL | Foo::func(); | ^^^^^^^^^^^ | -help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the function is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | const fn implicitly_stable_const_context() { - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(const_trait_impl)] LL | const fn implicitly_stable_const_context() { | @@ -344,14 +264,9 @@ error: const function that might be (indirectly) exposed to stable cannot use `# LL | Foo::func(); | ^^^^^^^^^^^ | -help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the function is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | const fn implicitly_stable_const_context() { - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(unstable)] LL | const fn implicitly_stable_const_context() { | @@ -362,14 +277,9 @@ LL | const_context_not_const_stable(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unstable features -help: if the caller is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the caller is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | const fn implicitly_stable_const_context() { - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(local_feature)] LL | const fn implicitly_stable_const_context() { | @@ -379,14 +289,9 @@ error: const function that might be (indirectly) exposed to stable cannot use `# LL | conditionally_const::(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the function is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | const fn implicitly_stable_const_context() { - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(const_trait_impl)] LL | const fn implicitly_stable_const_context() { |