From 8bbc080cc71f4af39225d9b0f3e70cf478347f15 Mon Sep 17 00:00:00 2001 From: Nathaniel Woods Date: Tue, 4 Mar 2025 16:33:21 -0500 Subject: [PATCH 01/18] Added `Clone` implementation for `ChunkBy` --- library/alloctests/tests/slice.rs | 13 +++++++++++++ library/core/src/slice/iter.rs | 7 +++++++ 2 files changed, 20 insertions(+) diff --git a/library/alloctests/tests/slice.rs b/library/alloctests/tests/slice.rs index 2516563187f2d..1e15d54d979a2 100644 --- a/library/alloctests/tests/slice.rs +++ b/library/alloctests/tests/slice.rs @@ -1636,6 +1636,19 @@ fn test_chunk_by() { assert_eq!(iter.next_back(), Some(&[1][..])); assert_eq!(iter.next(), Some(&[2, 2, 2][..])); assert_eq!(iter.next_back(), None); + + let mut iter = slice.chunk_by(|a, b| a == b); + assert_eq!(iter.next(), Some(&[1, 1, 1][..])); + assert_eq!(iter.next(), Some(&[3, 3][..])); + let mut iter_clone = iter.clone(); + assert_eq!(iter.next(), Some(&[2, 2, 2][..])); + assert_eq!(iter.next(), Some(&[1][..])); + assert_eq!(iter.next(), Some(&[0][..])); + assert_eq!(iter.next(), None); + assert_eq!(iter_clone.next(), Some(&[2, 2, 2][..])); + assert_eq!(iter_clone.next(), Some(&[1][..])); + assert_eq!(iter_clone.next(), Some(&[0][..])); + assert_eq!(iter_clone.next(), None); } #[test] diff --git a/library/core/src/slice/iter.rs b/library/core/src/slice/iter.rs index a687ed7129dc8..bf48068b25457 100644 --- a/library/core/src/slice/iter.rs +++ b/library/core/src/slice/iter.rs @@ -3372,6 +3372,13 @@ where #[stable(feature = "slice_group_by", since = "1.77.0")] impl<'a, T: 'a, P> FusedIterator for ChunkBy<'a, T, P> where P: FnMut(&T, &T) -> bool {} +#[stable(feature = "slice_group_by", since = "1.77.0")] +impl<'a, T: 'a, P: Clone> Clone for ChunkBy<'a, T, P> { + fn clone(&self) -> Self { + Self { slice: self.slice, predicate: self.predicate.clone() } + } +} + #[stable(feature = "slice_group_by", since = "1.77.0")] impl<'a, T: 'a + fmt::Debug, P> fmt::Debug for ChunkBy<'a, T, P> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { From e48617f817a2528502c7f6a59255e46c63d933d9 Mon Sep 17 00:00:00 2001 From: Nathaniel Woods Date: Thu, 6 Mar 2025 12:33:01 -0500 Subject: [PATCH 02/18] Changing #[stable] tag --- library/core/src/slice/iter.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/slice/iter.rs b/library/core/src/slice/iter.rs index bf48068b25457..8cbc6d4832eb8 100644 --- a/library/core/src/slice/iter.rs +++ b/library/core/src/slice/iter.rs @@ -3372,7 +3372,7 @@ where #[stable(feature = "slice_group_by", since = "1.77.0")] impl<'a, T: 'a, P> FusedIterator for ChunkBy<'a, T, P> where P: FnMut(&T, &T) -> bool {} -#[stable(feature = "slice_group_by", since = "1.77.0")] +#[stable(feature = "slice_group_by_clone", since = "CURRENT_RUSTC_VERSION")] impl<'a, T: 'a, P: Clone> Clone for ChunkBy<'a, T, P> { fn clone(&self) -> Self { Self { slice: self.slice, predicate: self.predicate.clone() } From c0851d76c95723b25712eb418b4c728b2980c60c Mon Sep 17 00:00:00 2001 From: zachs18 <8355914+zachs18@users.noreply.github.com> Date: Tue, 3 Jun 2025 00:22:10 -0500 Subject: [PATCH 03/18] Update ABI compatibility docs about null-pointer-optimized enums. Add that the enum must be `#[repr(Rust)]` and not `#[repr(packed)]` or `#[repr(align)]` in order to be ABI-compatible with its null-pointer-optimized field. --- library/core/src/primitive_docs.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/library/core/src/primitive_docs.rs b/library/core/src/primitive_docs.rs index 10b11613f90fa..9a06e43093edb 100644 --- a/library/core/src/primitive_docs.rs +++ b/library/core/src/primitive_docs.rs @@ -1833,6 +1833,8 @@ mod prim_ref {} /// - If `T` is guaranteed to be subject to the [null pointer /// optimization](option/index.html#representation), and `E` is an enum satisfying the following /// requirements, then `T` and `E` are ABI-compatible. Such an enum `E` is called "option-like". +/// - The enum `E` uses the [`Rust` representation], and is not modified by the `align` or +/// `packed` representation modifiers. /// - The enum `E` has exactly two variants. /// - One variant has exactly one field, of type `T`. /// - All fields of the other variant are zero-sized with 1-byte alignment. @@ -1906,6 +1908,7 @@ mod prim_ref {} /// [`Pointer`]: fmt::Pointer /// [`UnwindSafe`]: panic::UnwindSafe /// [`RefUnwindSafe`]: panic::RefUnwindSafe +/// [`Rust` representation]: /// /// In addition, all *safe* function pointers implement [`Fn`], [`FnMut`], and [`FnOnce`], because /// these traits are specially known to the compiler. From 959d6de1a928061256bb845025597be11ddca2ea Mon Sep 17 00:00:00 2001 From: mejrs <59372212+mejrs@users.noreply.github.com> Date: Sun, 18 May 2025 18:35:13 +0200 Subject: [PATCH 04/18] refactor `AttributeGate` and `rustc_attr!` to emit notes during feature checking --- compiler/rustc_ast_passes/src/feature_gate.rs | 14 +- compiler/rustc_feature/src/builtin_attrs.rs | 209 ++++++++++-------- compiler/rustc_lint/messages.ftl | 4 +- compiler/rustc_lint/src/builtin.rs | 21 +- compiler/rustc_lint/src/lints.rs | 8 +- compiler/rustc_session/src/parse.rs | 9 +- tests/ui/consts/gate-do-not-const-check.rs | 4 +- .../ui/consts/gate-do-not-const-check.stderr | 5 +- .../feature-gate-pattern-complexity-limit.rs | 4 +- ...ature-gate-pattern-complexity-limit.stderr | 5 +- .../feature-gate-rustc-attrs-1.rs | 12 +- .../feature-gate-rustc-attrs-1.stderr | 14 +- .../feature-gates/feature-gate-rustc-attrs.rs | 6 +- .../feature-gate-rustc-attrs.stderr | 15 +- ...sue-43106-gating-of-builtin-attrs-error.rs | 6 +- ...43106-gating-of-builtin-attrs-error.stderr | 75 +++---- tests/ui/force-inlining/gate.rs | 8 +- tests/ui/force-inlining/gate.stderr | 12 +- .../feature-gate-on-unimplemented.rs | 7 +- .../feature-gate-on-unimplemented.stderr | 5 +- .../ui/rustdoc/feature-gate-doc_primitive.rs | 4 +- .../rustdoc/feature-gate-doc_primitive.stderr | 5 +- tests/ui/tool-attributes/diagnostic_item.rs | 4 +- .../ui/tool-attributes/diagnostic_item.stderr | 4 +- .../rustc_must_implement_one_of_gated.rs | 4 +- .../rustc_must_implement_one_of_gated.stderr | 5 +- 26 files changed, 274 insertions(+), 195 deletions(-) diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 915613a391374..2f2f30415860d 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -36,6 +36,16 @@ macro_rules! gate_alt { feature_err(&$visitor.sess, $name, $span, $explain).emit(); } }}; + ($visitor:expr, $has_feature:expr, $name:expr, $span:expr, $explain:expr, $notes: expr) => {{ + if !$has_feature && !$span.allows_unstable($name) { + #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable + let mut diag = feature_err(&$visitor.sess, $name, $span, $explain); + for note in $notes { + diag.note(*note); + } + diag.emit(); + } + }}; } /// The case involving a multispan. @@ -154,11 +164,11 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { let attr_info = attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name)); // Check feature gates for built-in attributes. if let Some(BuiltinAttribute { - gate: AttributeGate::Gated(_, name, descr, has_feature), + gate: AttributeGate::Gated { feature, message, check, notes, .. }, .. }) = attr_info { - gate_alt!(self, has_feature(self.features), *name, attr.span, *descr); + gate_alt!(self, check(self.features), *feature, attr.span, *message, *notes); } // Check unstable flavors of the `#[doc]` attribute. if attr.has_name(sym::doc) { diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index c117e0fcf7ccc..aceaa2e5ab9a3 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -94,31 +94,27 @@ pub enum AttributeSafety { Unsafe { unsafe_since: Option }, } -#[derive(Clone, Copy)] +#[derive(Clone, Debug, Copy)] pub enum AttributeGate { - /// Is gated by a given feature gate, reason - /// and function to check if enabled - Gated(Stability, Symbol, &'static str, fn(&Features) -> bool), - + /// A gated attribute which requires a feature gate to be enabled. + Gated { + stability: Stability, + /// The feature gate, for example `#![feature(rustc_attrs)]` for rustc_* attributes. + feature: Symbol, + /// The error message displayed when an attempt is made to use the attribute without its feature gate. + message: &'static str, + /// Check function to be called during the `PostExpansionVisitor` pass. + check: fn(&Features) -> bool, + /// Notes to be displayed when an attempt is made to use the attribute without its feature gate. + notes: &'static [&'static str], + }, /// Ungated attribute, can be used on all release channels Ungated, } -// fn() is not Debug -impl std::fmt::Debug for AttributeGate { - fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match *self { - Self::Gated(ref stab, name, expl, _) => { - write!(fmt, "Gated({stab:?}, {name}, {expl})") - } - Self::Ungated => write!(fmt, "Ungated"), - } - } -} - impl AttributeGate { fn is_deprecated(&self) -> bool { - matches!(*self, Self::Gated(Stability::Deprecated(_, _), ..)) + matches!(*self, Self::Gated { stability: Stability::Deprecated(_, _), .. }) } } @@ -247,7 +243,7 @@ macro_rules! ungated { } macro_rules! gated { - (unsafe $attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr, $gate:ident, $msg:expr $(,)?) => { + (unsafe $attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr, $gate:ident, $message:expr $(,)?) => { BuiltinAttribute { name: sym::$attr, encode_cross_crate: $encode_cross_crate, @@ -255,10 +251,16 @@ macro_rules! gated { safety: AttributeSafety::Unsafe { unsafe_since: None }, template: $tpl, duplicates: $duplicates, - gate: Gated(Stability::Unstable, sym::$gate, $msg, Features::$gate), + gate: Gated { + stability: Stability::Unstable, + feature: sym::$gate, + message: $message, + check: Features::$gate, + notes: &[], + }, } }; - (unsafe $attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr, $msg:expr $(,)?) => { + (unsafe $attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr, $message:expr $(,)?) => { BuiltinAttribute { name: sym::$attr, encode_cross_crate: $encode_cross_crate, @@ -266,10 +268,16 @@ macro_rules! gated { safety: AttributeSafety::Unsafe { unsafe_since: None }, template: $tpl, duplicates: $duplicates, - gate: Gated(Stability::Unstable, sym::$attr, $msg, Features::$attr), + gate: Gated { + stability: Stability::Unstable, + feature: sym::$attr, + message: $message, + check: Features::$attr, + notes: &[], + }, } }; - ($attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr, $gate:ident, $msg:expr $(,)?) => { + ($attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr, $gate:ident, $message:expr $(,)?) => { BuiltinAttribute { name: sym::$attr, encode_cross_crate: $encode_cross_crate, @@ -277,10 +285,16 @@ macro_rules! gated { safety: AttributeSafety::Normal, template: $tpl, duplicates: $duplicates, - gate: Gated(Stability::Unstable, sym::$gate, $msg, Features::$gate), + gate: Gated { + stability: Stability::Unstable, + feature: sym::$gate, + message: $message, + check: Features::$gate, + notes: &[], + }, } }; - ($attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr, $msg:expr $(,)?) => { + ($attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr, $message:expr $(,)?) => { BuiltinAttribute { name: sym::$attr, encode_cross_crate: $encode_cross_crate, @@ -288,7 +302,13 @@ macro_rules! gated { safety: AttributeSafety::Normal, template: $tpl, duplicates: $duplicates, - gate: Gated(Stability::Unstable, sym::$attr, $msg, Features::$attr), + gate: Gated { + stability: Stability::Unstable, + feature: sym::$attr, + message: $message, + check: Features::$attr, + notes: &[], + }, } }; } @@ -304,12 +324,11 @@ macro_rules! rustc_attr { concat!( "the `#[", stringify!($attr), - "]` attribute is just used for rustc unit tests \ - and will never be stable", + "]` attribute is used for rustc unit tests" ), ) }; - ($attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr, $msg:expr $(,)?) => { + ($attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr, $($notes:expr),* $(,)?) => { BuiltinAttribute { name: sym::$attr, encode_cross_crate: $encode_cross_crate, @@ -317,7 +336,18 @@ macro_rules! rustc_attr { safety: AttributeSafety::Normal, template: $tpl, duplicates: $duplicates, - gate: Gated(Stability::Unstable, sym::rustc_attrs, $msg, Features::rustc_attrs), + gate: Gated { + stability: Stability::Unstable, + feature: sym::rustc_attrs, + message: "use of an internal attribute", + check: Features::rustc_attrs, + notes: &[ + concat!("the `#[", + stringify!($attr), + "]` attribute is an internal implementation detail that will never be stable"), + $($notes),* + ] + }, } }; } @@ -328,9 +358,6 @@ macro_rules! experimental { }; } -const IMPL_DETAIL: &str = "internal implementation detail"; -const INTERNAL_UNSTABLE: &str = "this is an internal attribute that will never be stable"; - #[derive(PartialEq)] pub enum EncodeCrossCrate { Yes, @@ -668,7 +695,7 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ rustc_attr!( rustc_deprecated_safe_2024, Normal, template!(List: r#"audit_that = "...""#), ErrorFollowing, EncodeCrossCrate::Yes, - "rustc_deprecated_safe_2024 is supposed to be used in libstd only", + "`#[rustc_deprecated_safe_2024]` is used to declare functions unsafe across the edition 2024 boundary", ), rustc_attr!( rustc_pub_transparent, Normal, template!(Word), @@ -695,7 +722,7 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ ErrorFollowing, EncodeCrossCrate::No, "`rustc_never_type_options` is used to experiment with never type fallback and work on \ - never type stabilization, and will never be stable" + never type stabilization" ), // ========================================================================== @@ -704,23 +731,23 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ rustc_attr!( rustc_allocator, Normal, template!(Word), WarnFollowing, - EncodeCrossCrate::No, IMPL_DETAIL + EncodeCrossCrate::No, ), rustc_attr!( rustc_nounwind, Normal, template!(Word), WarnFollowing, - EncodeCrossCrate::No, IMPL_DETAIL + EncodeCrossCrate::No, ), rustc_attr!( rustc_reallocator, Normal, template!(Word), WarnFollowing, - EncodeCrossCrate::No, IMPL_DETAIL + EncodeCrossCrate::No, ), rustc_attr!( rustc_deallocator, Normal, template!(Word), WarnFollowing, - EncodeCrossCrate::No, IMPL_DETAIL + EncodeCrossCrate::No, ), rustc_attr!( rustc_allocator_zeroed, Normal, template!(Word), WarnFollowing, - EncodeCrossCrate::No, IMPL_DETAIL + EncodeCrossCrate::No, ), gated!( default_lib_allocator, Normal, template!(Word), WarnFollowing, @@ -762,7 +789,7 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ ), rustc_attr!( rustc_std_internal_symbol, Normal, template!(Word), WarnFollowing, - EncodeCrossCrate::No, INTERNAL_UNSTABLE + EncodeCrossCrate::No, ), // ========================================================================== @@ -772,11 +799,11 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ rustc_attr!( rustc_builtin_macro, Normal, template!(Word, List: "name, /*opt*/ attributes(name1, name2, ...)"), ErrorFollowing, - EncodeCrossCrate::Yes, IMPL_DETAIL + EncodeCrossCrate::Yes, ), rustc_attr!( rustc_proc_macro_decls, Normal, template!(Word), WarnFollowing, - EncodeCrossCrate::No, INTERNAL_UNSTABLE + EncodeCrossCrate::No, ), rustc_attr!( rustc_macro_transparency, Normal, @@ -786,7 +813,7 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ rustc_attr!( rustc_autodiff, Normal, template!(Word, List: r#""...""#), DuplicatesOk, - EncodeCrossCrate::Yes, INTERNAL_UNSTABLE + EncodeCrossCrate::Yes, ), // Traces that are left when `cfg` and `cfg_attr` attributes are expanded. // The attributes are not gated, to avoid stability errors, but they cannot be used in stable @@ -812,54 +839,53 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ NameValueStr: "message" ), ErrorFollowing, EncodeCrossCrate::Yes, - INTERNAL_UNSTABLE + "see `#[diagnostic::on_unimplemented]` for the stable equivalent of this attribute" ), rustc_attr!( rustc_confusables, Normal, template!(List: r#""name1", "name2", ..."#), ErrorFollowing, EncodeCrossCrate::Yes, - INTERNAL_UNSTABLE, ), // Enumerates "identity-like" conversion methods to suggest on type mismatch. rustc_attr!( rustc_conversion_suggestion, Normal, template!(Word), - WarnFollowing, EncodeCrossCrate::Yes, INTERNAL_UNSTABLE + WarnFollowing, EncodeCrossCrate::Yes, ), // Prevents field reads in the marked trait or method to be considered // during dead code analysis. rustc_attr!( rustc_trivial_field_reads, Normal, template!(Word), - WarnFollowing, EncodeCrossCrate::Yes, INTERNAL_UNSTABLE + WarnFollowing, EncodeCrossCrate::Yes, ), // Used by the `rustc::potential_query_instability` lint to warn methods which // might not be stable during incremental compilation. rustc_attr!( rustc_lint_query_instability, Normal, template!(Word), - WarnFollowing, EncodeCrossCrate::Yes, INTERNAL_UNSTABLE + WarnFollowing, EncodeCrossCrate::Yes, ), // Used by the `rustc::untracked_query_information` lint to warn methods which // might not be stable during incremental compilation. rustc_attr!( rustc_lint_untracked_query_information, Normal, template!(Word), - WarnFollowing, EncodeCrossCrate::Yes, INTERNAL_UNSTABLE + WarnFollowing, EncodeCrossCrate::Yes, ), // Used by the `rustc::diagnostic_outside_of_impl` lints to assist in changes to diagnostic // APIs. Any function with this attribute will be checked by that lint. rustc_attr!( rustc_lint_diagnostics, Normal, template!(Word), - WarnFollowing, EncodeCrossCrate::Yes, INTERNAL_UNSTABLE + WarnFollowing, EncodeCrossCrate::Yes, ), // Used by the `rustc::bad_opt_access` lint to identify `DebuggingOptions` and `CodegenOptions` // types (as well as any others in future). rustc_attr!( rustc_lint_opt_ty, Normal, template!(Word), - WarnFollowing, EncodeCrossCrate::Yes, INTERNAL_UNSTABLE + WarnFollowing, EncodeCrossCrate::Yes, ), // Used by the `rustc::bad_opt_access` lint on fields // types (as well as any others in future). rustc_attr!( rustc_lint_opt_deny_field_access, Normal, template!(List: "message"), - WarnFollowing, EncodeCrossCrate::Yes, INTERNAL_UNSTABLE + WarnFollowing, EncodeCrossCrate::Yes, ), // ========================================================================== @@ -868,28 +894,30 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ rustc_attr!( rustc_promotable, Normal, template!(Word), WarnFollowing, - EncodeCrossCrate::No, IMPL_DETAIL), + EncodeCrossCrate::No, ), rustc_attr!( rustc_legacy_const_generics, Normal, template!(List: "N"), ErrorFollowing, - EncodeCrossCrate::Yes, INTERNAL_UNSTABLE + EncodeCrossCrate::Yes, ), // Do not const-check this function's body. It will always get replaced during CTFE. rustc_attr!( rustc_do_not_const_check, Normal, template!(Word), WarnFollowing, - EncodeCrossCrate::Yes, INTERNAL_UNSTABLE + EncodeCrossCrate::Yes, "`#[rustc_do_not_const_check]` skips const-check for this function's body", ), - // Ensure the argument to this function is &&str during const-check. rustc_attr!( rustc_const_panic_str, Normal, template!(Word), WarnFollowing, - EncodeCrossCrate::Yes, INTERNAL_UNSTABLE + EncodeCrossCrate::Yes, "`#[rustc_const_panic_str]` ensures the argument to this function is &&str during const-check", ), rustc_attr!( rustc_const_stable_indirect, Normal, - template!(Word), WarnFollowing, EncodeCrossCrate::No, IMPL_DETAIL, + template!(Word), + WarnFollowing, + EncodeCrossCrate::No, + "this is an internal implementation detail", ), rustc_attr!( rustc_intrinsic_const_stable_indirect, Normal, - template!(Word), WarnFollowing, EncodeCrossCrate::No, IMPL_DETAIL, + template!(Word), WarnFollowing, EncodeCrossCrate::No, "this is an internal implementation detail", ), gated!( rustc_allow_const_fn_unstable, Normal, @@ -905,21 +933,21 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ rustc_layout_scalar_valid_range_start, Normal, template!(List: "value"), ErrorFollowing, EncodeCrossCrate::Yes, "the `#[rustc_layout_scalar_valid_range_start]` attribute is just used to enable \ - niche optimizations in libcore and libstd and will never be stable", + niche optimizations in the standard library", ), rustc_attr!( rustc_layout_scalar_valid_range_end, Normal, template!(List: "value"), ErrorFollowing, EncodeCrossCrate::Yes, "the `#[rustc_layout_scalar_valid_range_end]` attribute is just used to enable \ - niche optimizations in libcore and libstd and will never be stable", + niche optimizations in the standard library", ), rustc_attr!( rustc_nonnull_optimization_guaranteed, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::Yes, "the `#[rustc_nonnull_optimization_guaranteed]` attribute is just used to document \ - guaranteed niche optimizations in libcore and libstd and will never be stable\n\ - (note that the compiler does not even check whether the type indeed is being non-null-optimized; \ - it is your responsibility to ensure that the attribute is only used on types that are optimized)", + guaranteed niche optimizations in the standard library", + "the compiler does not even check whether the type indeed is being non-null-optimized; \ + it is your responsibility to ensure that the attribute is only used on types that are optimized", ), // ========================================================================== @@ -932,17 +960,17 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ rustc_attr!( rustc_as_ptr, Normal, template!(Word), ErrorFollowing, EncodeCrossCrate::Yes, - "#[rustc_as_ptr] is used to mark functions returning pointers to their inner allocations." + "`#[rustc_as_ptr]` is used to mark functions returning pointers to their inner allocations." ), rustc_attr!( rustc_pass_by_value, Normal, template!(Word), ErrorFollowing, EncodeCrossCrate::Yes, - "#[rustc_pass_by_value] is used to mark types that must be passed by value instead of reference." + "`#[rustc_pass_by_value]` is used to mark types that must be passed by value instead of reference." ), rustc_attr!( rustc_never_returns_null_ptr, Normal, template!(Word), ErrorFollowing, EncodeCrossCrate::Yes, - "#[rustc_never_returns_null_ptr] is used to mark functions returning non-null pointers." + "`#[rustc_never_returns_null_ptr]` is used to mark functions returning non-null pointers." ), rustc_attr!( rustc_no_implicit_autorefs, AttributeType::Normal, template!(Word), ErrorFollowing, EncodeCrossCrate::Yes, @@ -950,15 +978,15 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ ), rustc_attr!( rustc_coherence_is_core, AttributeType::CrateLevel, template!(Word), ErrorFollowing, EncodeCrossCrate::No, - "#![rustc_coherence_is_core] allows inherent methods on builtin types, only intended to be used in `core`." + "`#![rustc_coherence_is_core]` allows inherent methods on builtin types, only intended to be used in `core`." ), rustc_attr!( rustc_coinductive, AttributeType::Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No, - "#![rustc_coinductive] changes a trait to be coinductive, allowing cycles in the trait solver." + "`#[rustc_coinductive]` changes a trait to be coinductive, allowing cycles in the trait solver." ), rustc_attr!( rustc_allow_incoherent_impl, AttributeType::Normal, template!(Word), ErrorFollowing, EncodeCrossCrate::No, - "#[rustc_allow_incoherent_impl] has to be added to all impl items of an incoherent inherent impl." + "`#[rustc_allow_incoherent_impl]` has to be added to all impl items of an incoherent inherent impl." ), rustc_attr!( rustc_preserve_ub_checks, AttributeType::CrateLevel, template!(Word), ErrorFollowing, EncodeCrossCrate::No, @@ -970,7 +998,7 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ template!(Word), ErrorFollowing, EncodeCrossCrate::No, - "#[rustc_deny_explicit_impl] enforces that a trait can have no user-provided impls" + "`#[rustc_deny_explicit_impl]` enforces that a trait can have no user-provided impls" ), rustc_attr!( rustc_do_not_implement_via_object, @@ -978,14 +1006,14 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ template!(Word), ErrorFollowing, EncodeCrossCrate::No, - "#[rustc_do_not_implement_via_object] opts out of the automatic trait impl for trait objects \ + "`#[rustc_do_not_implement_via_object]` opts out of the automatic trait impl for trait objects \ (`impl Trait for dyn Trait`)" ), rustc_attr!( rustc_has_incoherent_inherent_impls, AttributeType::Normal, template!(Word), ErrorFollowing, EncodeCrossCrate::Yes, - "#[rustc_has_incoherent_inherent_impls] allows the addition of incoherent inherent impls for \ - the given type by annotating all impl items with #[rustc_allow_incoherent_impl]." + "`#[rustc_has_incoherent_inherent_impls]` allows the addition of incoherent inherent impls for \ + the given type by annotating all impl items with `#[rustc_allow_incoherent_impl]`." ), BuiltinAttribute { @@ -996,12 +1024,14 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ safety: AttributeSafety::Normal, template: template!(NameValueStr: "name"), duplicates: ErrorFollowing, - gate: Gated( - Stability::Unstable, - sym::rustc_attrs, - "diagnostic items compiler internal support for linting", - Features::rustc_attrs, - ), + gate: Gated{ + stability: Stability::Unstable, + feature: sym::rustc_attrs, + message: "use of an internal attribute", + check: Features::rustc_attrs, + notes: &["the `#[rustc_diagnostic_item]` attribute allows the compiler to reference types \ + from the standard library for diagnostic purposes"], + }, }, gated!( // Used in resolve: @@ -1015,14 +1045,14 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ rustc_attr!( rustc_inherit_overflow_checks, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No, "the `#[rustc_inherit_overflow_checks]` attribute is just used to control \ - overflow checking behavior of several libcore functions that are inlined \ - across crates and will never be stable", + overflow checking behavior of several functions in the standard library that are inlined \ + across crates", ), rustc_attr!( rustc_reservation_impl, Normal, template!(NameValueStr: "reservation message"), ErrorFollowing, EncodeCrossCrate::Yes, "the `#[rustc_reservation_impl]` attribute is internally used \ - for reserving for `for From for T` impl" + for reserving `impl From for T` as part of the effort to stabilize `!`" ), rustc_attr!( rustc_test_marker, Normal, template!(NameValueStr: "name"), WarnFollowing, @@ -1053,12 +1083,13 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ rustc_must_implement_one_of, Normal, template!(List: "function1, function2, ..."), ErrorFollowing, EncodeCrossCrate::No, "the `#[rustc_must_implement_one_of]` attribute is used to change minimal complete \ - definition of a trait, it's currently in experimental form and should be changed before \ - being exposed outside of the std" + definition of a trait. Its syntax and semantics are highly experimental and will be \ + subject to change before stabilization", ), rustc_attr!( rustc_doc_primitive, Normal, template!(NameValueStr: "primitive name"), ErrorFollowing, - EncodeCrossCrate::Yes, r#"`rustc_doc_primitive` is a rustc internal attribute"#, + EncodeCrossCrate::Yes, "the `#[rustc_doc_primitive]` attribute is used by the standard library \ + to provide a way to generate documentation for primitive types", ), gated!( rustc_intrinsic, Normal, template!(Word), ErrorFollowing, EncodeCrossCrate::Yes, intrinsics, @@ -1066,11 +1097,11 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ ), rustc_attr!( rustc_no_mir_inline, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::Yes, - "#[rustc_no_mir_inline] prevents the MIR inliner from inlining a function while not affecting codegen" + "`#[rustc_no_mir_inline]` prevents the MIR inliner from inlining a function while not affecting codegen" ), rustc_attr!( rustc_force_inline, Normal, template!(Word, NameValueStr: "reason"), WarnFollowing, EncodeCrossCrate::Yes, - "#[rustc_force_inline] forces a free function to be inlined" + "`#[rustc_force_inline]` forces a free function to be inlined" ), // ========================================================================== diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 17485a838f31f..46818af59f5c2 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -72,8 +72,8 @@ lint_builtin_const_no_mangle = const items should never be `#[no_mangle]` lint_builtin_decl_unsafe_fn = declaration of an `unsafe` function lint_builtin_decl_unsafe_method = declaration of an `unsafe` method -lint_builtin_deprecated_attr_link = use of deprecated attribute `{$name}`: {$reason}. See {$link} - .msg_suggestion = {$msg} +lint_builtin_deprecated_attr_link = use of deprecated attribute `{$name}`: {$message}. See {$link} + .msg_suggestion = {$suggestion} .default_suggestion = remove this attribute lint_builtin_deref_nullptr = dereferencing a null pointer .label = this code causes undefined behavior when executed diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 47b80135bae94..2e09ef75be4fd 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -818,25 +818,22 @@ impl EarlyLintPass for DeprecatedAttr { fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &ast::Attribute) { for BuiltinAttribute { name, gate, .. } in &self.depr_attrs { if attr.ident().map(|ident| ident.name) == Some(*name) { - if let &AttributeGate::Gated( - Stability::Deprecated(link, suggestion), - name, - reason, - _, - ) = gate + if let &AttributeGate::Gated { + stability: Stability::Deprecated(link, suggestion), + message, + .. + } = gate { let suggestion = match suggestion { - Some(msg) => { - BuiltinDeprecatedAttrLinkSuggestion::Msg { suggestion: attr.span, msg } - } - None => { - BuiltinDeprecatedAttrLinkSuggestion::Default { suggestion: attr.span } + Some(suggestion) => { + BuiltinDeprecatedAttrLinkSuggestion::Msg { span: attr.span, suggestion } } + None => BuiltinDeprecatedAttrLinkSuggestion::Default { span: attr.span }, }; cx.emit_span_lint( DEPRECATED, attr.span, - BuiltinDeprecatedAttrLink { name, reason, link, suggestion }, + BuiltinDeprecatedAttrLink { name: *name, message, link, suggestion }, ); } return; diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 10d0e2c93a83a..82b88ec721518 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -204,7 +204,7 @@ pub(crate) struct BuiltinAnonymousParams<'a> { #[diag(lint_builtin_deprecated_attr_link)] pub(crate) struct BuiltinDeprecatedAttrLink<'a> { pub name: Symbol, - pub reason: &'a str, + pub message: &'a str, pub link: &'a str, #[subdiagnostic] pub suggestion: BuiltinDeprecatedAttrLinkSuggestion<'a>, @@ -215,13 +215,13 @@ pub(crate) enum BuiltinDeprecatedAttrLinkSuggestion<'a> { #[suggestion(lint_msg_suggestion, code = "", applicability = "machine-applicable")] Msg { #[primary_span] - suggestion: Span, - msg: &'a str, + span: Span, + suggestion: &'a str, }, #[suggestion(lint_default_suggestion, code = "", applicability = "machine-applicable")] Default { #[primary_span] - suggestion: Span, + span: Span, }, } diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs index 1fe521bd32d54..87c848cf857f8 100644 --- a/compiler/rustc_session/src/parse.rs +++ b/compiler/rustc_session/src/parse.rs @@ -17,7 +17,7 @@ use rustc_feature::{GateIssue, UnstableFeatures, find_feature_issue}; use rustc_span::edition::Edition; use rustc_span::hygiene::ExpnId; use rustc_span::source_map::{FilePathMapping, SourceMap}; -use rustc_span::{Span, Symbol}; +use rustc_span::{Span, Symbol, sym}; use crate::Session; use crate::config::{Cfg, CheckCfg}; @@ -192,8 +192,11 @@ pub fn add_feature_diagnostics_for_issue( } else { err.subdiagnostic(FeatureDiagnosticHelp { feature }); } - - if sess.opts.unstable_opts.ui_testing { + if feature == sym::rustc_attrs { + // We're unlikely to stabilize something out of `rustc_attrs` + // without at least renaming it, so pointing out how old + // the compiler is will do little good. + } else if sess.opts.unstable_opts.ui_testing { err.subdiagnostic(SuggestUpgradeCompiler::ui_testing()); } else if let Some(suggestion) = SuggestUpgradeCompiler::new() { err.subdiagnostic(suggestion); diff --git a/tests/ui/consts/gate-do-not-const-check.rs b/tests/ui/consts/gate-do-not-const-check.rs index be7e70dfabba7..0ebb1e7c82e2b 100644 --- a/tests/ui/consts/gate-do-not-const-check.rs +++ b/tests/ui/consts/gate-do-not-const-check.rs @@ -1,5 +1,7 @@ #[rustc_do_not_const_check] -//~^ ERROR this is an internal attribute that will never be stable +//~^ ERROR use of an internal attribute [E0658] +//~| NOTE the `#[rustc_do_not_const_check]` attribute is an internal implementation detail that will never be stable +//~| NOTE `#[rustc_do_not_const_check]` skips const-check for this function's body const fn foo() {} fn main() {} diff --git a/tests/ui/consts/gate-do-not-const-check.stderr b/tests/ui/consts/gate-do-not-const-check.stderr index 74ea71c4ed801..778ee50e71b6d 100644 --- a/tests/ui/consts/gate-do-not-const-check.stderr +++ b/tests/ui/consts/gate-do-not-const-check.stderr @@ -1,11 +1,12 @@ -error[E0658]: this is an internal attribute that will never be stable +error[E0658]: use of an internal attribute --> $DIR/gate-do-not-const-check.rs:1:1 | LL | #[rustc_do_not_const_check] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = note: the `#[rustc_do_not_const_check]` attribute is an internal implementation detail that will never be stable + = note: `#[rustc_do_not_const_check]` skips const-check for this function's body error: aborting due to 1 previous error diff --git a/tests/ui/feature-gates/feature-gate-pattern-complexity-limit.rs b/tests/ui/feature-gates/feature-gate-pattern-complexity-limit.rs index d1f6f4755f08a..ffb444cd9aaf4 100644 --- a/tests/ui/feature-gates/feature-gate-pattern-complexity-limit.rs +++ b/tests/ui/feature-gates/feature-gate-pattern-complexity-limit.rs @@ -1,6 +1,8 @@ // check that `pattern_complexity_limit` is feature-gated #![pattern_complexity_limit = "42"] -//~^ ERROR: the `#[pattern_complexity_limit]` attribute is just used for rustc unit tests +//~^ ERROR: use of an internal attribute [E0658] +//~| NOTE the `#[pattern_complexity_limit]` attribute is an internal implementation detail that will never be stable +//~| NOTE: the `#[pattern_complexity_limit]` attribute is used for rustc unit tests fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-pattern-complexity-limit.stderr b/tests/ui/feature-gates/feature-gate-pattern-complexity-limit.stderr index e6f17710e0960..9ddea866ea997 100644 --- a/tests/ui/feature-gates/feature-gate-pattern-complexity-limit.stderr +++ b/tests/ui/feature-gates/feature-gate-pattern-complexity-limit.stderr @@ -1,11 +1,12 @@ -error[E0658]: the `#[pattern_complexity_limit]` attribute is just used for rustc unit tests and will never be stable +error[E0658]: use of an internal attribute --> $DIR/feature-gate-pattern-complexity-limit.rs:3:1 | LL | #![pattern_complexity_limit = "42"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = note: the `#[pattern_complexity_limit]` attribute is an internal implementation detail that will never be stable + = note: the `#[pattern_complexity_limit]` attribute is used for rustc unit tests error: aborting due to 1 previous error diff --git a/tests/ui/feature-gates/feature-gate-rustc-attrs-1.rs b/tests/ui/feature-gates/feature-gate-rustc-attrs-1.rs index 025b4b52f12fe..17556723622ed 100644 --- a/tests/ui/feature-gates/feature-gate-rustc-attrs-1.rs +++ b/tests/ui/feature-gates/feature-gate-rustc-attrs-1.rs @@ -1,6 +1,12 @@ // Test that `#[rustc_*]` attributes are gated by `rustc_attrs` feature gate. -#[rustc_variance] //~ ERROR the `#[rustc_variance]` attribute is just used for rustc unit tests and will never be stable -#[rustc_nonnull_optimization_guaranteed] //~ ERROR the `#[rustc_nonnull_optimization_guaranteed]` attribute is just used to document guaranteed niche optimizations in libcore and libstd and will never be stable - +#[rustc_variance] +//~^ ERROR use of an internal attribute [E0658] +//~| NOTE the `#[rustc_variance]` attribute is an internal implementation detail that will never be stable +//~| NOTE the `#[rustc_variance]` attribute is used for rustc unit tests +#[rustc_nonnull_optimization_guaranteed] +//~^ ERROR use of an internal attribute [E0658] +//~| NOTE the `#[rustc_nonnull_optimization_guaranteed]` attribute is an internal implementation detail that will never be stable +//~| NOTE the `#[rustc_nonnull_optimization_guaranteed]` attribute is just used to document guaranteed niche optimizations in the standard library +//~| NOTE the compiler does not even check whether the type indeed is being non-null-optimized; it is your responsibility to ensure that the attribute is only used on types that are optimized fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-rustc-attrs-1.stderr b/tests/ui/feature-gates/feature-gate-rustc-attrs-1.stderr index 0f760e0602d2f..159d383e40891 100644 --- a/tests/ui/feature-gates/feature-gate-rustc-attrs-1.stderr +++ b/tests/ui/feature-gates/feature-gate-rustc-attrs-1.stderr @@ -1,21 +1,23 @@ -error[E0658]: the `#[rustc_variance]` attribute is just used for rustc unit tests and will never be stable +error[E0658]: use of an internal attribute --> $DIR/feature-gate-rustc-attrs-1.rs:3:1 | LL | #[rustc_variance] | ^^^^^^^^^^^^^^^^^ | = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = note: the `#[rustc_variance]` attribute is an internal implementation detail that will never be stable + = note: the `#[rustc_variance]` attribute is used for rustc unit tests -error[E0658]: the `#[rustc_nonnull_optimization_guaranteed]` attribute is just used to document guaranteed niche optimizations in libcore and libstd and will never be stable - (note that the compiler does not even check whether the type indeed is being non-null-optimized; it is your responsibility to ensure that the attribute is only used on types that are optimized) - --> $DIR/feature-gate-rustc-attrs-1.rs:4:1 +error[E0658]: use of an internal attribute + --> $DIR/feature-gate-rustc-attrs-1.rs:7:1 | LL | #[rustc_nonnull_optimization_guaranteed] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = note: the `#[rustc_nonnull_optimization_guaranteed]` attribute is an internal implementation detail that will never be stable + = note: the `#[rustc_nonnull_optimization_guaranteed]` attribute is just used to document guaranteed niche optimizations in the standard library + = note: the compiler does not even check whether the type indeed is being non-null-optimized; it is your responsibility to ensure that the attribute is only used on types that are optimized error: aborting due to 2 previous errors diff --git a/tests/ui/feature-gates/feature-gate-rustc-attrs.rs b/tests/ui/feature-gates/feature-gate-rustc-attrs.rs index c985298a30aed..e7b2eca6f858a 100644 --- a/tests/ui/feature-gates/feature-gate-rustc-attrs.rs +++ b/tests/ui/feature-gates/feature-gate-rustc-attrs.rs @@ -8,15 +8,19 @@ mod unknown { pub macro rustc() {} } #[rustc::unknown] //~^ ERROR attributes starting with `rustc` are reserved for use by the `rustc` compiler //~| ERROR expected attribute, found macro `rustc::unknown` +//~| NOTE not an attribute fn f() {} #[unknown::rustc] //~^ ERROR attributes starting with `rustc` are reserved for use by the `rustc` compiler //~| ERROR expected attribute, found macro `unknown::rustc` +//~| NOTE not an attribute fn g() {} #[rustc_dummy] -//~^ ERROR the `#[rustc_dummy]` attribute is just used for rustc unit tests +//~^ ERROR use of an internal attribute [E0658] +//~| NOTE the `#[rustc_dummy]` attribute is an internal implementation detail that will never be stable +//~| NOTE the `#[rustc_dummy]` attribute is used for rustc unit tests #[rustc_unknown] //~^ ERROR attributes starting with `rustc` are reserved for use by the `rustc` compiler //~| ERROR cannot find attribute `rustc_unknown` in this scope diff --git a/tests/ui/feature-gates/feature-gate-rustc-attrs.stderr b/tests/ui/feature-gates/feature-gate-rustc-attrs.stderr index c7a5ef3e44be7..d58603883f158 100644 --- a/tests/ui/feature-gates/feature-gate-rustc-attrs.stderr +++ b/tests/ui/feature-gates/feature-gate-rustc-attrs.stderr @@ -11,37 +11,38 @@ LL | #[rustc::unknown] | ^^^^^^^^^^^^^^ not an attribute error: attributes starting with `rustc` are reserved for use by the `rustc` compiler - --> $DIR/feature-gate-rustc-attrs.rs:13:12 + --> $DIR/feature-gate-rustc-attrs.rs:14:12 | LL | #[unknown::rustc] | ^^^^^ error: expected attribute, found macro `unknown::rustc` - --> $DIR/feature-gate-rustc-attrs.rs:13:3 + --> $DIR/feature-gate-rustc-attrs.rs:14:3 | LL | #[unknown::rustc] | ^^^^^^^^^^^^^^ not an attribute error: attributes starting with `rustc` are reserved for use by the `rustc` compiler - --> $DIR/feature-gate-rustc-attrs.rs:20:3 + --> $DIR/feature-gate-rustc-attrs.rs:24:3 | LL | #[rustc_unknown] | ^^^^^^^^^^^^^ error: cannot find attribute `rustc_unknown` in this scope - --> $DIR/feature-gate-rustc-attrs.rs:20:3 + --> $DIR/feature-gate-rustc-attrs.rs:24:3 | LL | #[rustc_unknown] | ^^^^^^^^^^^^^ -error[E0658]: the `#[rustc_dummy]` attribute is just used for rustc unit tests and will never be stable - --> $DIR/feature-gate-rustc-attrs.rs:18:1 +error[E0658]: use of an internal attribute + --> $DIR/feature-gate-rustc-attrs.rs:20:1 | LL | #[rustc_dummy] | ^^^^^^^^^^^^^^ | = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = note: the `#[rustc_dummy]` attribute is an internal implementation detail that will never be stable + = note: the `#[rustc_dummy]` attribute is used for rustc unit tests error: aborting due to 7 previous errors diff --git a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.rs b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.rs index 02a56c7e6aa6c..7fb11b7bde73b 100644 --- a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.rs +++ b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.rs @@ -11,9 +11,11 @@ #![macro_export] //~^ ERROR: `macro_export` attribute cannot be used at crate level -#![rustc_main] //~ ERROR: the `#[rustc_main]` attribute is used internally to specify +#![rustc_main] //~^ ERROR: `rustc_main` attribute cannot be used at crate level -//~| NOTE: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date +//~| ERROR: use of an internal attribute [E0658] +//~| NOTE: the `#[rustc_main]` attribute is an internal implementation detail that will never be stable +//~| NOTE: the `#[rustc_main]` attribute is used internally to specify test entry point function #![repr()] //~^ ERROR: `repr` attribute cannot be used at crate level #![path = "3800"] diff --git a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.stderr b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.stderr index 5c2a3ae699c03..bdca6163473c6 100644 --- a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.stderr +++ b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.stderr @@ -1,14 +1,15 @@ -error[E0658]: the `#[rustc_main]` attribute is used internally to specify test entry point function +error[E0658]: use of an internal attribute --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:14:1 | LL | #![rustc_main] | ^^^^^^^^^^^^^^ | = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = note: the `#[rustc_main]` attribute is an internal implementation detail that will never be stable + = note: the `#[rustc_main]` attribute is used internally to specify test entry point function error: valid forms for the attribute are `#[inline]` and `#[inline(always|never)]` - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:44:5 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:46:5 | LL | #[inline = "2100"] fn f() { } | ^^^^^^^^^^^^^^^^^^ @@ -18,7 +19,7 @@ LL | #[inline = "2100"] fn f() { } = note: `#[deny(ill_formed_attribute_input)]` on by default error[E0518]: attribute should be applied to function or closure - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:30:1 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:32:1 | LL | #[inline] | ^^^^^^^^^ @@ -29,7 +30,7 @@ LL | | } | |_- not a function or closure error: attribute should be applied to an `extern crate` item - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:63:1 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:65:1 | LL | #[no_link] | ^^^^^^^^^^ @@ -43,7 +44,7 @@ LL | | } | |_- not an `extern crate` item error: attribute should be applied to a free function, impl method or static - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:89:1 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:91:1 | LL | #[export_name = "2200"] | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -57,7 +58,7 @@ LL | | } | |_- not a free function, impl method or static error[E0517]: attribute should be applied to a struct, enum, or union - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:123:8 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:125:8 | LL | #[repr(C)] | ^ @@ -70,7 +71,7 @@ LL | | } | |_- not a struct, enum, or union error[E0517]: attribute should be applied to a struct, enum, or union - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:147:8 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:149:8 | LL | #[repr(Rust)] | ^^^^ @@ -83,19 +84,19 @@ LL | | } | |_- not a struct, enum, or union error: attribute should be applied to an `extern crate` item - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:24:1 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:26:1 | LL | #![no_link] | ^^^^^^^^^^^ not an `extern crate` item error: attribute should be applied to a free function, impl method or static - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:26:1 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:28:1 | LL | #![export_name = "2200"] | ^^^^^^^^^^^^^^^^^^^^^^^^ not a free function, impl method or static error[E0518]: attribute should be applied to function or closure - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:28:1 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:30:1 | LL | #![inline] | ^^^^^^^^^^ not a function or closure @@ -131,7 +132,7 @@ LL + #[rustc_main] | error: `path` attribute cannot be used at crate level - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:19:1 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:21:1 | LL | #![path = "3800"] | ^^^^^^^^^^^^^^^^^ @@ -146,7 +147,7 @@ LL + #[path = "3800"] | error: `automatically_derived` attribute cannot be used at crate level - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:21:1 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:23:1 | LL | #![automatically_derived] | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -161,7 +162,7 @@ LL + #[automatically_derived] | error: `repr` attribute cannot be used at crate level - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:17:1 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:19:1 | LL | #![repr()] | ^^^^^^^^^^ @@ -176,139 +177,139 @@ LL + #[repr()] | error[E0518]: attribute should be applied to function or closure - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:40:17 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:42:17 | LL | mod inner { #![inline] } | ------------^^^^^^^^^^-- not a function or closure error[E0518]: attribute should be applied to function or closure - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:50:5 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:52:5 | LL | #[inline] struct S; | ^^^^^^^^^ --------- not a function or closure error[E0518]: attribute should be applied to function or closure - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:54:5 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:56:5 | LL | #[inline] type T = S; | ^^^^^^^^^ ----------- not a function or closure error[E0518]: attribute should be applied to function or closure - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:58:5 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:60:5 | LL | #[inline] impl S { } | ^^^^^^^^^ ---------- not a function or closure error: attribute should be applied to an `extern crate` item - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:68:17 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:70:17 | LL | mod inner { #![no_link] } | ------------^^^^^^^^^^^-- not an `extern crate` item error: attribute should be applied to an `extern crate` item - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:72:5 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:74:5 | LL | #[no_link] fn f() { } | ^^^^^^^^^^ ---------- not an `extern crate` item error: attribute should be applied to an `extern crate` item - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:76:5 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:78:5 | LL | #[no_link] struct S; | ^^^^^^^^^^ --------- not an `extern crate` item error: attribute should be applied to an `extern crate` item - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:80:5 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:82:5 | LL | #[no_link]type T = S; | ^^^^^^^^^^----------- not an `extern crate` item error: attribute should be applied to an `extern crate` item - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:84:5 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:86:5 | LL | #[no_link] impl S { } | ^^^^^^^^^^ ---------- not an `extern crate` item error: attribute should be applied to a free function, impl method or static - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:94:17 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:96:17 | LL | mod inner { #![export_name="2200"] } | ------------^^^^^^^^^^^^^^^^^^^^^^-- not a free function, impl method or static error: attribute should be applied to a free function, impl method or static - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:100:5 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:102:5 | LL | #[export_name = "2200"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^^ --------- not a free function, impl method or static error: attribute should be applied to a free function, impl method or static - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:104:5 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:106:5 | LL | #[export_name = "2200"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^^ ----------- not a free function, impl method or static error: attribute should be applied to a free function, impl method or static - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:108:5 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:110:5 | LL | #[export_name = "2200"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^^ ---------- not a free function, impl method or static error: attribute should be applied to a free function, impl method or static - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:113:9 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:115:9 | LL | #[export_name = "2200"] fn foo(); | ^^^^^^^^^^^^^^^^^^^^^^^ --------- not a free function, impl method or static error: attribute should be applied to a free function, impl method or static - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:117:9 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:119:9 | LL | #[export_name = "2200"] fn bar() {} | ^^^^^^^^^^^^^^^^^^^^^^^ ----------- not a free function, impl method or static error[E0517]: attribute should be applied to a struct, enum, or union - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:127:25 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:129:25 | LL | mod inner { #![repr(C)] } | --------------------^---- not a struct, enum, or union error[E0517]: attribute should be applied to a struct, enum, or union - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:131:12 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:133:12 | LL | #[repr(C)] fn f() { } | ^ ---------- not a struct, enum, or union error[E0517]: attribute should be applied to a struct, enum, or union - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:137:12 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:139:12 | LL | #[repr(C)] type T = S; | ^ ----------- not a struct, enum, or union error[E0517]: attribute should be applied to a struct, enum, or union - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:141:12 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:143:12 | LL | #[repr(C)] impl S { } | ^ ---------- not a struct, enum, or union error[E0517]: attribute should be applied to a struct, enum, or union - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:151:25 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:153:25 | LL | mod inner { #![repr(Rust)] } | --------------------^^^^---- not a struct, enum, or union error[E0517]: attribute should be applied to a struct, enum, or union - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:155:12 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:157:12 | LL | #[repr(Rust)] fn f() { } | ^^^^ ---------- not a struct, enum, or union error[E0517]: attribute should be applied to a struct, enum, or union - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:161:12 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:163:12 | LL | #[repr(Rust)] type T = S; | ^^^^ ----------- not a struct, enum, or union error[E0517]: attribute should be applied to a struct, enum, or union - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:165:12 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:167:12 | LL | #[repr(Rust)] impl S { } | ^^^^ ---------- not a struct, enum, or union diff --git a/tests/ui/force-inlining/gate.rs b/tests/ui/force-inlining/gate.rs index cea094c14f2d6..5918b0d497902 100644 --- a/tests/ui/force-inlining/gate.rs +++ b/tests/ui/force-inlining/gate.rs @@ -2,11 +2,15 @@ #![allow(internal_features)] #[rustc_force_inline] -//~^ ERROR #[rustc_force_inline] forces a free function to be inlined +//~^ ERROR use of an internal attribute [E0658] +//~| NOTE the `#[rustc_force_inline]` attribute is an internal implementation detail that will never be stable +//~| NOTE `#[rustc_force_inline]` forces a free function to be inlined pub fn bare() { } #[rustc_force_inline = "the test requires it"] -//~^ ERROR #[rustc_force_inline] forces a free function to be inlined +//~^ ERROR use of an internal attribute [E0658] +//~| NOTE the `#[rustc_force_inline]` attribute is an internal implementation detail that will never be stable +//~| NOTE `#[rustc_force_inline]` forces a free function to be inlined pub fn justified() { } diff --git a/tests/ui/force-inlining/gate.stderr b/tests/ui/force-inlining/gate.stderr index 964d43fa18f6a..6c2df08b1a3f1 100644 --- a/tests/ui/force-inlining/gate.stderr +++ b/tests/ui/force-inlining/gate.stderr @@ -1,20 +1,22 @@ -error[E0658]: #[rustc_force_inline] forces a free function to be inlined +error[E0658]: use of an internal attribute --> $DIR/gate.rs:4:1 | LL | #[rustc_force_inline] | ^^^^^^^^^^^^^^^^^^^^^ | = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = note: the `#[rustc_force_inline]` attribute is an internal implementation detail that will never be stable + = note: `#[rustc_force_inline]` forces a free function to be inlined -error[E0658]: #[rustc_force_inline] forces a free function to be inlined - --> $DIR/gate.rs:9:1 +error[E0658]: use of an internal attribute + --> $DIR/gate.rs:11:1 | LL | #[rustc_force_inline = "the test requires it"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = note: the `#[rustc_force_inline]` attribute is an internal implementation detail that will never be stable + = note: `#[rustc_force_inline]` forces a free function to be inlined error: aborting due to 2 previous errors diff --git a/tests/ui/on-unimplemented/feature-gate-on-unimplemented.rs b/tests/ui/on-unimplemented/feature-gate-on-unimplemented.rs index 3cc50e3499a09..436caab5d64aa 100644 --- a/tests/ui/on-unimplemented/feature-gate-on-unimplemented.rs +++ b/tests/ui/on-unimplemented/feature-gate-on-unimplemented.rs @@ -1,8 +1,9 @@ // Test that `#[rustc_on_unimplemented]` is gated by `rustc_attrs` feature gate. #[rustc_on_unimplemented = "test error `{Self}` with `{Bar}`"] -//~^ ERROR this is an internal attribute that will never be stable -trait Foo -{} +//~^ ERROR use of an internal attribute [E0658] +//~| NOTE the `#[rustc_on_unimplemented]` attribute is an internal implementation detail that will never be stable +//~| NOTE see `#[diagnostic::on_unimplemented]` for the stable equivalent of this attribute +trait Foo {} fn main() {} diff --git a/tests/ui/on-unimplemented/feature-gate-on-unimplemented.stderr b/tests/ui/on-unimplemented/feature-gate-on-unimplemented.stderr index 2733f7478f0a5..d1983088af853 100644 --- a/tests/ui/on-unimplemented/feature-gate-on-unimplemented.stderr +++ b/tests/ui/on-unimplemented/feature-gate-on-unimplemented.stderr @@ -1,11 +1,12 @@ -error[E0658]: this is an internal attribute that will never be stable +error[E0658]: use of an internal attribute --> $DIR/feature-gate-on-unimplemented.rs:3:1 | LL | #[rustc_on_unimplemented = "test error `{Self}` with `{Bar}`"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = note: the `#[rustc_on_unimplemented]` attribute is an internal implementation detail that will never be stable + = note: see `#[diagnostic::on_unimplemented]` for the stable equivalent of this attribute error: aborting due to 1 previous error diff --git a/tests/ui/rustdoc/feature-gate-doc_primitive.rs b/tests/ui/rustdoc/feature-gate-doc_primitive.rs index 78fcd90752e46..dbf92f19378af 100644 --- a/tests/ui/rustdoc/feature-gate-doc_primitive.rs +++ b/tests/ui/rustdoc/feature-gate-doc_primitive.rs @@ -1,5 +1,7 @@ #[rustc_doc_primitive = "usize"] -//~^ ERROR `rustc_doc_primitive` is a rustc internal attribute +//~^ ERROR use of an internal attribute [E0658] +//~| NOTE the `#[rustc_doc_primitive]` attribute is an internal implementation detail that will never be stable +//~| NOTE the `#[rustc_doc_primitive]` attribute is used by the standard library to provide a way to generate documentation for primitive types /// Some docs mod usize {} diff --git a/tests/ui/rustdoc/feature-gate-doc_primitive.stderr b/tests/ui/rustdoc/feature-gate-doc_primitive.stderr index e74b1322b2597..0b1af78b50410 100644 --- a/tests/ui/rustdoc/feature-gate-doc_primitive.stderr +++ b/tests/ui/rustdoc/feature-gate-doc_primitive.stderr @@ -1,11 +1,12 @@ -error[E0658]: `rustc_doc_primitive` is a rustc internal attribute +error[E0658]: use of an internal attribute --> $DIR/feature-gate-doc_primitive.rs:1:1 | LL | #[rustc_doc_primitive = "usize"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = note: the `#[rustc_doc_primitive]` attribute is an internal implementation detail that will never be stable + = note: the `#[rustc_doc_primitive]` attribute is used by the standard library to provide a way to generate documentation for primitive types error: aborting due to 1 previous error diff --git a/tests/ui/tool-attributes/diagnostic_item.rs b/tests/ui/tool-attributes/diagnostic_item.rs index 26a52ce60cfd8..806b140feba38 100644 --- a/tests/ui/tool-attributes/diagnostic_item.rs +++ b/tests/ui/tool-attributes/diagnostic_item.rs @@ -1,3 +1,5 @@ -#[rustc_diagnostic_item = "foomp"] //~ ERROR compiler internal support for linting +#[rustc_diagnostic_item = "foomp"] +//~^ ERROR use of an internal attribute [E0658] +//~| NOTE the `#[rustc_diagnostic_item]` attribute allows the compiler to reference types from the standard library for diagnostic purposes struct Foomp; fn main() {} diff --git a/tests/ui/tool-attributes/diagnostic_item.stderr b/tests/ui/tool-attributes/diagnostic_item.stderr index c6ae5a38594fd..d37044281a136 100644 --- a/tests/ui/tool-attributes/diagnostic_item.stderr +++ b/tests/ui/tool-attributes/diagnostic_item.stderr @@ -1,11 +1,11 @@ -error[E0658]: diagnostic items compiler internal support for linting +error[E0658]: use of an internal attribute --> $DIR/diagnostic_item.rs:1:1 | LL | #[rustc_diagnostic_item = "foomp"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = note: the `#[rustc_diagnostic_item]` attribute allows the compiler to reference types from the standard library for diagnostic purposes error: aborting due to 1 previous error diff --git a/tests/ui/traits/default-method/rustc_must_implement_one_of_gated.rs b/tests/ui/traits/default-method/rustc_must_implement_one_of_gated.rs index 27e70556b7af8..47cc9f5f96056 100644 --- a/tests/ui/traits/default-method/rustc_must_implement_one_of_gated.rs +++ b/tests/ui/traits/default-method/rustc_must_implement_one_of_gated.rs @@ -1,5 +1,7 @@ #[rustc_must_implement_one_of(eq, neq)] -//~^ ERROR the `#[rustc_must_implement_one_of]` attribute is used to change minimal complete definition of a trait, it's currently in experimental form and should be changed before being exposed outside of the std +//~^ ERROR use of an internal attribute [E0658] +//~| NOTE the `#[rustc_must_implement_one_of]` attribute is an internal implementation detail that will never be stable +//~| NOTE the `#[rustc_must_implement_one_of]` attribute is used to change minimal complete definition of a trait. Its syntax and semantics are highly experimental and will be subject to change before stabilization trait Equal { fn eq(&self, other: &Self) -> bool { !self.neq(other) diff --git a/tests/ui/traits/default-method/rustc_must_implement_one_of_gated.stderr b/tests/ui/traits/default-method/rustc_must_implement_one_of_gated.stderr index 162c3d36cb1c6..4d44ceff98a4e 100644 --- a/tests/ui/traits/default-method/rustc_must_implement_one_of_gated.stderr +++ b/tests/ui/traits/default-method/rustc_must_implement_one_of_gated.stderr @@ -1,11 +1,12 @@ -error[E0658]: the `#[rustc_must_implement_one_of]` attribute is used to change minimal complete definition of a trait, it's currently in experimental form and should be changed before being exposed outside of the std +error[E0658]: use of an internal attribute --> $DIR/rustc_must_implement_one_of_gated.rs:1:1 | LL | #[rustc_must_implement_one_of(eq, neq)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = note: the `#[rustc_must_implement_one_of]` attribute is an internal implementation detail that will never be stable + = note: the `#[rustc_must_implement_one_of]` attribute is used to change minimal complete definition of a trait. Its syntax and semantics are highly experimental and will be subject to change before stabilization error: aborting due to 1 previous error From 81eb182a02770922ce8513545c976004984a42ec Mon Sep 17 00:00:00 2001 From: mejrs <59372212+mejrs@users.noreply.github.com> Date: Fri, 6 Jun 2025 11:11:42 +0200 Subject: [PATCH 05/18] Remove rustc_feature::Stability --- compiler/rustc_feature/src/builtin_attrs.rs | 19 +------- compiler/rustc_feature/src/lib.rs | 13 +----- compiler/rustc_lint/messages.ftl | 3 -- compiler/rustc_lint/src/builtin.rs | 49 +-------------------- compiler/rustc_lint/src/lib.rs | 1 - compiler/rustc_lint/src/lints.rs | 26 ----------- 6 files changed, 5 insertions(+), 106 deletions(-) diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index aceaa2e5ab9a3..73a21789c5d25 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -9,7 +9,7 @@ use rustc_data_structures::fx::FxHashMap; use rustc_span::edition::Edition; use rustc_span::{Symbol, sym}; -use crate::{Features, Stability}; +use crate::Features; type GateFn = fn(&Features) -> bool; @@ -98,7 +98,6 @@ pub enum AttributeSafety { pub enum AttributeGate { /// A gated attribute which requires a feature gate to be enabled. Gated { - stability: Stability, /// The feature gate, for example `#![feature(rustc_attrs)]` for rustc_* attributes. feature: Symbol, /// The error message displayed when an attempt is made to use the attribute without its feature gate. @@ -112,12 +111,6 @@ pub enum AttributeGate { Ungated, } -impl AttributeGate { - fn is_deprecated(&self) -> bool { - matches!(*self, Self::Gated { stability: Stability::Deprecated(_, _), .. }) - } -} - /// A template that the attribute input must match. /// Only top-level shape (`#[attr]` vs `#[attr(...)]` vs `#[attr = ...]`) is considered now. #[derive(Clone, Copy, Default)] @@ -252,7 +245,6 @@ macro_rules! gated { template: $tpl, duplicates: $duplicates, gate: Gated { - stability: Stability::Unstable, feature: sym::$gate, message: $message, check: Features::$gate, @@ -269,7 +261,6 @@ macro_rules! gated { template: $tpl, duplicates: $duplicates, gate: Gated { - stability: Stability::Unstable, feature: sym::$attr, message: $message, check: Features::$attr, @@ -286,7 +277,6 @@ macro_rules! gated { template: $tpl, duplicates: $duplicates, gate: Gated { - stability: Stability::Unstable, feature: sym::$gate, message: $message, check: Features::$gate, @@ -303,7 +293,6 @@ macro_rules! gated { template: $tpl, duplicates: $duplicates, gate: Gated { - stability: Stability::Unstable, feature: sym::$attr, message: $message, check: Features::$attr, @@ -337,7 +326,6 @@ macro_rules! rustc_attr { template: $tpl, duplicates: $duplicates, gate: Gated { - stability: Stability::Unstable, feature: sym::rustc_attrs, message: "use of an internal attribute", check: Features::rustc_attrs, @@ -1025,7 +1013,6 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ template: template!(NameValueStr: "name"), duplicates: ErrorFollowing, gate: Gated{ - stability: Stability::Unstable, feature: sym::rustc_attrs, message: "use of an internal attribute", check: Features::rustc_attrs, @@ -1240,10 +1227,6 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ ), ]; -pub fn deprecated_attributes() -> Vec<&'static BuiltinAttribute> { - BUILTIN_ATTRIBUTES.iter().filter(|attr| attr.gate.is_deprecated()).collect() -} - pub fn is_builtin_attr_name(name: Symbol) -> bool { BUILTIN_ATTRIBUTE_MAP.get(&name).is_some() } diff --git a/compiler/rustc_feature/src/lib.rs b/compiler/rustc_feature/src/lib.rs index 25764755a8fc9..dbc0daa3d8383 100644 --- a/compiler/rustc_feature/src/lib.rs +++ b/compiler/rustc_feature/src/lib.rs @@ -40,14 +40,6 @@ pub struct Feature { issue: Option>, } -#[derive(Copy, Clone, Debug)] -pub enum Stability { - Unstable, - // First argument is tracking issue link; second argument is an optional - // help message, which defaults to "remove this attribute". - Deprecated(&'static str, Option<&'static str>), -} - #[derive(Clone, Copy, Debug, Hash)] pub enum UnstableFeatures { /// Disallow use of unstable features, as on beta/stable channels. @@ -144,9 +136,8 @@ pub fn find_feature_issue(feature: Symbol, issue: GateIssue) -> Option, -} - -impl_lint_pass!(DeprecatedAttr => []); - -impl Default for DeprecatedAttr { - fn default() -> Self { - DeprecatedAttr { depr_attrs: deprecated_attributes() } - } -} - -impl EarlyLintPass for DeprecatedAttr { - fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &ast::Attribute) { - for BuiltinAttribute { name, gate, .. } in &self.depr_attrs { - if attr.ident().map(|ident| ident.name) == Some(*name) { - if let &AttributeGate::Gated { - stability: Stability::Deprecated(link, suggestion), - message, - .. - } = gate - { - let suggestion = match suggestion { - Some(suggestion) => { - BuiltinDeprecatedAttrLinkSuggestion::Msg { span: attr.span, suggestion } - } - None => BuiltinDeprecatedAttrLinkSuggestion::Default { span: attr.span }, - }; - cx.emit_span_lint( - DEPRECATED, - attr.span, - BuiltinDeprecatedAttrLink { name: *name, message, link, suggestion }, - ); - } - return; - } - } - } -} - fn warn_if_doc(cx: &EarlyContext<'_>, node_span: Span, node_kind: &str, attrs: &[ast::Attribute]) { use rustc_ast::token::CommentKind; diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 0a52e42e44287..63c884f1ab9a8 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -172,7 +172,6 @@ early_lint_methods!( AnonymousParameters: AnonymousParameters, EllipsisInclusiveRangePatterns: EllipsisInclusiveRangePatterns::default(), NonCamelCaseTypes: NonCamelCaseTypes, - DeprecatedAttr: DeprecatedAttr::default(), WhileTrue: WhileTrue, NonAsciiIdents: NonAsciiIdents, IncompleteInternalFeatures: IncompleteInternalFeatures, diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 82b88ec721518..0a47393eb9994 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -199,32 +199,6 @@ pub(crate) struct BuiltinAnonymousParams<'a> { pub ty_snip: &'a str, } -// FIXME(davidtwco) translatable deprecated attr -#[derive(LintDiagnostic)] -#[diag(lint_builtin_deprecated_attr_link)] -pub(crate) struct BuiltinDeprecatedAttrLink<'a> { - pub name: Symbol, - pub message: &'a str, - pub link: &'a str, - #[subdiagnostic] - pub suggestion: BuiltinDeprecatedAttrLinkSuggestion<'a>, -} - -#[derive(Subdiagnostic)] -pub(crate) enum BuiltinDeprecatedAttrLinkSuggestion<'a> { - #[suggestion(lint_msg_suggestion, code = "", applicability = "machine-applicable")] - Msg { - #[primary_span] - span: Span, - suggestion: &'a str, - }, - #[suggestion(lint_default_suggestion, code = "", applicability = "machine-applicable")] - Default { - #[primary_span] - span: Span, - }, -} - #[derive(LintDiagnostic)] #[diag(lint_builtin_unused_doc_comment)] pub(crate) struct BuiltinUnusedDocComment<'a> { From 70f78fb2ba2b5ada70dfda2ea219483e545816c7 Mon Sep 17 00:00:00 2001 From: mejrs <59372212+mejrs@users.noreply.github.com> Date: Fri, 23 May 2025 18:52:18 +0200 Subject: [PATCH 06/18] Add test for flailing diagnostic spans --- .../diagnostic_namespace/multiline_spans.rs | 53 ++++++++++++++++ .../multiline_spans.stderr | 61 +++++++++++++++++++ 2 files changed, 114 insertions(+) create mode 100644 tests/ui/diagnostic_namespace/multiline_spans.rs create mode 100644 tests/ui/diagnostic_namespace/multiline_spans.stderr diff --git a/tests/ui/diagnostic_namespace/multiline_spans.rs b/tests/ui/diagnostic_namespace/multiline_spans.rs new file mode 100644 index 0000000000000..97dd07b2bf365 --- /dev/null +++ b/tests/ui/diagnostic_namespace/multiline_spans.rs @@ -0,0 +1,53 @@ +#![crate_type = "lib"] +#![deny(unknown_or_malformed_diagnostic_attributes)] + + +#[diagnostic::on_unimplemented(message = "here is a big \ + multiline string \ + {unknown}")] +//~^^ ERROR there is no parameter `unknown` on trait `MultiLine` [unknown_or_malformed_diagnostic_attributes] +pub trait MultiLine {} + +#[diagnostic::on_unimplemented(message = "here is a big \ + multiline string {unknown}")] +//~^ ERROR there is no parameter `unknown` on trait `MultiLine2` [unknown_or_malformed_diagnostic_attributes] +pub trait MultiLine2 {} + +#[diagnostic::on_unimplemented(message = "here is a big \ + multiline string {unknown}")] +//~^ ERROR there is no parameter `unknown` on trait `MultiLine3` [unknown_or_malformed_diagnostic_attributes] +pub trait MultiLine3 {} + + +#[diagnostic::on_unimplemented(message = "here is a big \ +\ + \ + \ + \ + multiline string {unknown}")] +//~^^^^ ERROR there is no parameter `unknown` on trait `MultiLine4` [unknown_or_malformed_diagnostic_attributes] +pub trait MultiLine4 {} + +#[diagnostic::on_unimplemented(message = "here is a big \ + multiline string \ + {Self:+}")] +//~^^^ ERROR invalid format specifier [unknown_or_malformed_diagnostic_attributes] +pub trait MultiLineFmt {} + +#[diagnostic::on_unimplemented(message = "here is a big \ + multiline string {Self:X}")] +pub trait MultiLineFmt2 {} + +#[diagnostic::on_unimplemented(message = "here is a big \ + multiline string {Self:#}")] +//~^^ ERROR invalid format specifier [unknown_or_malformed_diagnostic_attributes] +pub trait MultiLineFmt3 {} + + +#[diagnostic::on_unimplemented(message = "here is a big \ +\ + \ + \ + \ + multiline string {Self:?}")] +pub trait MultiLineFmt4 {} diff --git a/tests/ui/diagnostic_namespace/multiline_spans.stderr b/tests/ui/diagnostic_namespace/multiline_spans.stderr new file mode 100644 index 0000000000000..8d86560b66c10 --- /dev/null +++ b/tests/ui/diagnostic_namespace/multiline_spans.stderr @@ -0,0 +1,61 @@ +error: there is no parameter `unknown` on trait `MultiLine` + --> $DIR/multiline_spans.rs:6:17 + | +LL | ... multiline string \ + | ^^^^^^^ + | + = help: expect either a generic argument name or `{Self}` as format argument +note: the lint level is defined here + --> $DIR/multiline_spans.rs:2:9 + | +LL | #![deny(unknown_or_malformed_diagnostic_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: there is no parameter `unknown` on trait `MultiLine2` + --> $DIR/multiline_spans.rs:12:17 + | +LL | ... multiline string {unknown}")] + | ^^^^^^^ + | + = help: expect either a generic argument name or `{Self}` as format argument + +error: there is no parameter `unknown` on trait `MultiLine3` + --> $DIR/multiline_spans.rs:17:17 + | +LL | multiline string {unknown}")] + | ^^^^^^^ + | + = help: expect either a generic argument name or `{Self}` as format argument + +error: there is no parameter `unknown` on trait `MultiLine4` + --> $DIR/multiline_spans.rs:24:15 + | +LL | / \ +LL | | \ + | |___^ + | + = help: expect either a generic argument name or `{Self}` as format argument + +error: invalid format specifier + --> $DIR/multiline_spans.rs:31:42 + | +LL | #[diagnostic::on_unimplemented(message = "here is a big \ + | __________________________________________^ +LL | | multiline string \ +LL | | {Self:+}")] + | |__________________________________________________^ + | + = help: no format specifier are supported in this position + +error: invalid format specifier + --> $DIR/multiline_spans.rs:41:42 + | +LL | #[diagnostic::on_unimplemented(message = "here is a big \ + | __________________________________________^ +LL | | multiline string {Self:#}")] + | |______________________________^ + | + = help: no format specifier are supported in this position + +error: aborting due to 6 previous errors + From 716190632f58f073f3cac903324b62899599876b Mon Sep 17 00:00:00 2001 From: mejrs <59372212+mejrs@users.noreply.github.com> Date: Fri, 23 May 2025 19:48:18 +0200 Subject: [PATCH 07/18] rustc_parse_format: introduce `peek` and `peek_ahead` --- compiler/rustc_parse_format/src/lib.rs | 64 +++++++++++++++----------- 1 file changed, 36 insertions(+), 28 deletions(-) diff --git a/compiler/rustc_parse_format/src/lib.rs b/compiler/rustc_parse_format/src/lib.rs index 9dd064aca662b..358723a17ef17 100644 --- a/compiler/rustc_parse_format/src/lib.rs +++ b/compiler/rustc_parse_format/src/lib.rs @@ -241,11 +241,11 @@ impl<'a> Iterator for Parser<'a> { type Item = Piece<'a>; fn next(&mut self) -> Option> { - if let Some(&(Range { start, end }, idx, ch)) = self.input_vec.get(self.input_vec_index) { + if let Some((Range { start, end }, idx, ch)) = self.peek() { match ch { '{' => { self.input_vec_index += 1; - if let Some(&(_, i, '{')) = self.input_vec.get(self.input_vec_index) { + if let Some((_, i, '{')) = self.peek() { self.input_vec_index += 1; // double open brace escape: "{{" // next state after this is either end-of-input or seen-a-brace @@ -258,7 +258,7 @@ impl<'a> Iterator for Parser<'a> { if self.is_source_literal { self.arg_places.push(start..close_brace_range.end); } - } else if let Some(&(_, _, c)) = self.input_vec.get(self.input_vec_index) { + } else if let Some((_, _, c)) = self.peek() { match c { '?' => self.suggest_format_debug(), '<' | '^' | '>' => self.suggest_format_align(c), @@ -272,7 +272,7 @@ impl<'a> Iterator for Parser<'a> { } '}' => { self.input_vec_index += 1; - if let Some(&(_, i, '}')) = self.input_vec.get(self.input_vec_index) { + if let Some((_, i, '}')) = self.peek() { self.input_vec_index += 1; // double close brace escape: "}}" // next state after this is either end-of-input or start @@ -406,6 +406,16 @@ impl<'a> Parser<'a> { } } + /// Peeks at the current position, without incrementing the pointer. + pub fn peek(&self) -> Option<(Range, usize, char)> { + self.input_vec.get(self.input_vec_index).cloned() + } + + /// Peeks at the current position + 1, without incrementing the pointer. + pub fn peek_ahead(&self) -> Option<(Range, usize, char)> { + self.input_vec.get(self.input_vec_index + 1).cloned() + } + /// Optionally consumes the specified character. If the character is not at /// the current position, then the current iterator isn't moved and `false` is /// returned, otherwise the character is consumed and `true` is returned. @@ -418,10 +428,10 @@ impl<'a> Parser<'a> { /// returned, otherwise the character is consumed and the current position is /// returned. fn consume_pos(&mut self, ch: char) -> Option<(Range, usize)> { - if let Some((r, i, c)) = self.input_vec.get(self.input_vec_index) { - if ch == *c { + if let Some((r, i, c)) = self.peek() { + if ch == c { self.input_vec_index += 1; - return Some((r.clone(), *i)); + return Some((r, i)); } } None @@ -432,11 +442,10 @@ impl<'a> Parser<'a> { fn consume_closing_brace(&mut self, arg: &Argument<'_>) -> Option> { self.ws(); - let (range, description) = if let Some((r, _, c)) = self.input_vec.get(self.input_vec_index) - { - if *c == '}' { + let (range, description) = if let Some((r, _, c)) = self.peek() { + if c == '}' { self.input_vec_index += 1; - return Some(r.clone()); + return Some(r); } // or r.clone()? (r.start..r.start, format!("expected `}}`, found `{}`", c.escape_debug())) @@ -484,10 +493,10 @@ impl<'a> Parser<'a> { /// Parses all of a string which is to be considered a "raw literal" in a /// format string. This is everything outside of the braces. fn string(&mut self, start: usize) -> &'a str { - while let Some((r, i, c)) = self.input_vec.get(self.input_vec_index) { + while let Some((r, i, c)) = self.peek() { match c { '{' | '}' => { - return &self.input[start..*i]; + return &self.input[start..i]; } '\n' if self.is_source_literal => { self.input_vec_index += 1; @@ -540,19 +549,18 @@ impl<'a> Parser<'a> { if let Some(i) = self.integer() { Some(ArgumentIs(i.into())) } else { - match self.input_vec.get(self.input_vec_index) { - Some((range, _, c)) if rustc_lexer::is_id_start(*c) => { + match self.peek() { + Some((range, _, c)) if rustc_lexer::is_id_start(c) => { let start = range.start; let word = self.word(); // Recover from `r#ident` in format strings. // FIXME: use a let chain if word == "r" { - if let Some((r, _, '#')) = self.input_vec.get(self.input_vec_index) { + if let Some((r, _, '#')) = self.peek() { if self - .input_vec - .get(self.input_vec_index + 1) - .is_some_and(|(_, _, c)| rustc_lexer::is_id_start(*c)) + .peek_ahead() + .is_some_and(|(_, _, c)| rustc_lexer::is_id_start(c)) { self.input_vec_index += 1; let prefix_end = r.end; @@ -584,7 +592,7 @@ impl<'a> Parser<'a> { } fn input_vec_index2pos(&self, index: usize) -> usize { - if let Some(&(_, pos, _)) = self.input_vec.get(index) { pos } else { self.input.len() } + if let Some((_, pos, _)) = self.input_vec.get(index) { *pos } else { self.input.len() } } fn input_vec_index2range(&self, index: usize) -> Range { @@ -618,11 +626,11 @@ impl<'a> Parser<'a> { } // fill character - if let Some(&(ref r, _, c)) = self.input_vec.get(self.input_vec_index) { - if let Some((_, _, '>' | '<' | '^')) = self.input_vec.get(self.input_vec_index + 1) { + if let Some((r, _, c)) = self.peek() { + if let Some((_, _, '>' | '<' | '^')) = self.peek_ahead() { self.input_vec_index += 1; spec.fill = Some(c); - spec.fill_span = Some(r.clone()); + spec.fill_span = Some(r); } } // Alignment @@ -701,7 +709,7 @@ impl<'a> Parser<'a> { } } else if let Some((range, _)) = self.consume_pos('?') { spec.ty = "?"; - if let Some((r, _, c)) = self.input_vec.get(self.input_vec_index) { + if let Some((r, _, c)) = self.peek() { match c { '#' | 'x' | 'X' => self.errors.insert( 0, @@ -788,8 +796,8 @@ impl<'a> Parser<'a> { /// Rust identifier, except that it can't start with `_` character. fn word(&mut self) -> &'a str { let index = self.input_vec_index; - match self.input_vec.get(self.input_vec_index) { - Some(&(ref r, i, c)) if rustc_lexer::is_id_start(c) => { + match self.peek() { + Some((ref r, i, c)) if rustc_lexer::is_id_start(c) => { self.input_vec_index += 1; (r.start, i) } @@ -798,7 +806,7 @@ impl<'a> Parser<'a> { } }; let (err_end, end): (usize, usize) = loop { - if let Some(&(ref r, i, c)) = self.input_vec.get(self.input_vec_index) { + if let Some((ref r, i, c)) = self.peek() { if rustc_lexer::is_id_continue(c) { self.input_vec_index += 1; } else { @@ -828,7 +836,7 @@ impl<'a> Parser<'a> { let mut found = false; let mut overflow = false; let start_index = self.input_vec_index; - while let Some(&(_, _, c)) = self.input_vec.get(self.input_vec_index) { + while let Some((_, _, c)) = self.peek() { if let Some(i) = c.to_digit(10) { self.input_vec_index += 1; let (tmp, mul_overflow) = cur.overflowing_mul(10); From d3137d91b8bf87197757c5429d9acfcda79c0911 Mon Sep 17 00:00:00 2001 From: mejrs <59372212+mejrs@users.noreply.github.com> Date: Fri, 23 May 2025 20:48:22 +0200 Subject: [PATCH 08/18] Move the "missing closing brace" error creation to one place --- compiler/rustc_parse_format/src/lib.rs | 36 +++++++++++--------------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/compiler/rustc_parse_format/src/lib.rs b/compiler/rustc_parse_format/src/lib.rs index 358723a17ef17..9a45a392b773f 100644 --- a/compiler/rustc_parse_format/src/lib.rs +++ b/compiler/rustc_parse_format/src/lib.rs @@ -254,19 +254,15 @@ impl<'a> Iterator for Parser<'a> { // single open brace self.last_open_brace = Some(start..end); let arg = self.argument(); - if let Some(close_brace_range) = self.consume_closing_brace(&arg) { + self.ws(); + if let Some((close_brace_range, _)) = self.consume_pos('}') { if self.is_source_literal { self.arg_places.push(start..close_brace_range.end); } - } else if let Some((_, _, c)) = self.peek() { - match c { - '?' => self.suggest_format_debug(), - '<' | '^' | '>' => self.suggest_format_align(c), - _ => { - self.suggest_positional_arg_instead_of_captured_arg(arg.clone()) - } - } + } else { + self.missing_closing_brace(&arg); } + Some(Piece::NextArgument(Box::new(arg))) } } @@ -437,17 +433,9 @@ impl<'a> Parser<'a> { None } - /// Forces consumption of the specified character. If the character is not - /// found, an error is emitted. - fn consume_closing_brace(&mut self, arg: &Argument<'_>) -> Option> { - self.ws(); - + /// Called if a closing brace was not found. + fn missing_closing_brace(&mut self, arg: &Argument<'_>) { let (range, description) = if let Some((r, _, c)) = self.peek() { - if c == '}' { - self.input_vec_index += 1; - return Some(r); - } - // or r.clone()? (r.start..r.start, format!("expected `}}`, found `{}`", c.escape_debug())) } else { ( @@ -480,7 +468,13 @@ impl<'a> Parser<'a> { suggestion: Suggestion::None, }); - None + if let Some((_, _, c)) = self.peek() { + match c { + '?' => self.suggest_format_debug(), + '<' | '^' | '>' => self.suggest_format_align(c), + _ => self.suggest_positional_arg_instead_of_captured_arg(arg), + } + } } /// Consumes all whitespace characters until the first non-whitespace character @@ -905,7 +899,7 @@ impl<'a> Parser<'a> { } } - fn suggest_positional_arg_instead_of_captured_arg(&mut self, arg: Argument<'a>) { + fn suggest_positional_arg_instead_of_captured_arg(&mut self, arg: &Argument<'_>) { // If the argument is not an identifier, it is not a field access. if !arg.is_identifier() { return; From cf3af234e3a8753615f3546029bd4ae75f35150d Mon Sep 17 00:00:00 2001 From: mejrs <59372212+mejrs@users.noreply.github.com> Date: Fri, 23 May 2025 22:37:23 +0200 Subject: [PATCH 09/18] refactor matching and if let chains --- compiler/rustc_parse_format/src/lib.rs | 83 ++++++++++++-------------- 1 file changed, 37 insertions(+), 46 deletions(-) diff --git a/compiler/rustc_parse_format/src/lib.rs b/compiler/rustc_parse_format/src/lib.rs index 9a45a392b773f..372ebe3fdd3cf 100644 --- a/compiler/rustc_parse_format/src/lib.rs +++ b/compiler/rustc_parse_format/src/lib.rs @@ -424,12 +424,13 @@ impl<'a> Parser<'a> { /// returned, otherwise the character is consumed and the current position is /// returned. fn consume_pos(&mut self, ch: char) -> Option<(Range, usize)> { - if let Some((r, i, c)) = self.peek() { - if ch == c { - self.input_vec_index += 1; - return Some((r, i)); - } + if let Some((r, i, c)) = self.peek() + && ch == c + { + self.input_vec_index += 1; + return Some((r, i)); } + None } @@ -549,20 +550,17 @@ impl<'a> Parser<'a> { let word = self.word(); // Recover from `r#ident` in format strings. - // FIXME: use a let chain - if word == "r" { - if let Some((r, _, '#')) = self.peek() { - if self - .peek_ahead() - .is_some_and(|(_, _, c)| rustc_lexer::is_id_start(c)) - { - self.input_vec_index += 1; - let prefix_end = r.end; - let word = self.word(); - let prefix_span = start..prefix_end; - let full_span = - start..self.input_vec_index2range(self.input_vec_index).start; - self.errors.insert(0, ParseError { + if word == "r" + && let Some((r, _, '#')) = self.peek() + && self.peek_ahead().is_some_and(|(_, _, c)| rustc_lexer::is_id_start(c)) + { + self.input_vec_index += 1; + let prefix_end = r.end; + let word = self.word(); + let prefix_span = start..prefix_end; + let full_span = + start..self.input_vec_index2range(self.input_vec_index).start; + self.errors.insert(0, ParseError { description: "raw identifiers are not supported".to_owned(), note: Some("identifiers in format strings can be keywords and don't need to be prefixed with `r#`".to_string()), label: "raw identifier used here".to_owned(), @@ -570,9 +568,7 @@ impl<'a> Parser<'a> { secondary_label: None, suggestion: Suggestion::RemoveRawIdent(prefix_span), }); - return Some(ArgumentNamed(word)); - } - } + return Some(ArgumentNamed(word)); } Some(ArgumentNamed(word)) @@ -620,12 +616,10 @@ impl<'a> Parser<'a> { } // fill character - if let Some((r, _, c)) = self.peek() { - if let Some((_, _, '>' | '<' | '^')) = self.peek_ahead() { - self.input_vec_index += 1; - spec.fill = Some(c); - spec.fill_span = Some(r); - } + if let (Some((r, _, c)), Some((_, _, '>' | '<' | '^'))) = (self.peek(), self.peek_ahead()) { + self.input_vec_index += 1; + spec.fill = Some(c); + spec.fill_span = Some(r); } // Alignment if self.consume('<') { @@ -703,24 +697,21 @@ impl<'a> Parser<'a> { } } else if let Some((range, _)) = self.consume_pos('?') { spec.ty = "?"; - if let Some((r, _, c)) = self.peek() { - match c { - '#' | 'x' | 'X' => self.errors.insert( - 0, - ParseError { - description: format!("expected `}}`, found `{c}`"), - note: None, - label: "expected `'}'`".into(), - span: r.clone(), - secondary_label: None, - suggestion: Suggestion::ReorderFormatParameter( - range.start..r.end, - format!("{c}?"), - ), - }, - ), - _ => (), - } + if let Some((r, _, c @ ('#' | 'x' | 'X'))) = self.peek() { + self.errors.insert( + 0, + ParseError { + description: format!("expected `}}`, found `{c}`"), + note: None, + label: "expected `'}'`".into(), + span: r.clone(), + secondary_label: None, + suggestion: Suggestion::ReorderFormatParameter( + range.start..r.end, + format!("{c}?"), + ), + }, + ); } } else { spec.ty = self.word(); From b68c06b1e2fdfe9d9aebbd7aa1650248ec6f85a2 Mon Sep 17 00:00:00 2001 From: mejrs <59372212+mejrs@users.noreply.github.com> Date: Fri, 23 May 2025 22:51:40 +0200 Subject: [PATCH 10/18] implement Default for FormatSpec --- compiler/rustc_parse_format/src/lib.rs | 62 +++++--------------------- 1 file changed, 10 insertions(+), 52 deletions(-) diff --git a/compiler/rustc_parse_format/src/lib.rs b/compiler/rustc_parse_format/src/lib.rs index 372ebe3fdd3cf..6e99c75324eda 100644 --- a/compiler/rustc_parse_format/src/lib.rs +++ b/compiler/rustc_parse_format/src/lib.rs @@ -56,30 +56,12 @@ pub struct Argument<'a> { impl<'a> Argument<'a> { pub fn is_identifier(&self) -> bool { - matches!(self.position, Position::ArgumentNamed(_)) - && matches!( - self.format, - FormatSpec { - fill: None, - fill_span: None, - align: AlignUnknown, - sign: None, - alternate: false, - zero_pad: false, - debug_hex: None, - precision: CountImplied, - precision_span: None, - width: CountImplied, - width_span: None, - ty: "", - ty_span: None, - }, - ) + matches!(self.position, Position::ArgumentNamed(_)) && self.format == FormatSpec::default() } } /// Specification for the formatting of an argument in the format string. -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, Default)] pub struct FormatSpec<'a> { /// Optionally specified character to fill alignment with. pub fill: Option, @@ -132,7 +114,7 @@ impl Position<'_> { } /// Enum of alignments which are supported. -#[derive(Copy, Clone, Debug, PartialEq)] +#[derive(Copy, Clone, Debug, PartialEq, Default)] pub enum Alignment { /// The value will be aligned to the left. AlignLeft, @@ -141,6 +123,7 @@ pub enum Alignment { /// The value will be aligned in the center. AlignCenter, /// The value will take on a default alignment. + #[default] AlignUnknown, } @@ -164,7 +147,7 @@ pub enum DebugHex { /// A count is used for the precision and width parameters of an integer, and /// can reference either an argument or a literal integer. -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, Default)] pub enum Count<'a> { /// The count is specified explicitly. CountIs(u16), @@ -175,6 +158,7 @@ pub enum Count<'a> { /// The count is specified by a star (like in `{:.*}`) that refers to the argument at the given index. CountIsStar(usize), /// The count is implied and cannot be explicitly specified. + #[default] CountImplied, } @@ -596,21 +580,8 @@ impl<'a> Parser<'a> { /// Parses a format specifier at the current position, returning all of the /// relevant information in the `FormatSpec` struct. fn format(&mut self) -> FormatSpec<'a> { - let mut spec = FormatSpec { - fill: None, - fill_span: None, - align: AlignUnknown, - sign: None, - alternate: false, - zero_pad: false, - debug_hex: None, - precision: CountImplied, - precision_span: None, - width: CountImplied, - width_span: None, - ty: &self.input[..0], - ty_span: None, - }; + let mut spec = FormatSpec::default(); + if !self.consume(':') { return spec; } @@ -727,21 +698,8 @@ impl<'a> Parser<'a> { /// Parses an inline assembly template modifier at the current position, returning the modifier /// in the `ty` field of the `FormatSpec` struct. fn inline_asm(&mut self) -> FormatSpec<'a> { - let mut spec = FormatSpec { - fill: None, - fill_span: None, - align: AlignUnknown, - sign: None, - alternate: false, - zero_pad: false, - debug_hex: None, - precision: CountImplied, - precision_span: None, - width: CountImplied, - width_span: None, - ty: &self.input[..0], - ty_span: None, - }; + let mut spec = FormatSpec::default(); + if !self.consume(':') { return spec; } From f002abad5e78e2d7c90cac19d756774f24b029f2 Mon Sep 17 00:00:00 2001 From: mejrs <59372212+mejrs@users.noreply.github.com> Date: Fri, 23 May 2025 23:25:32 +0200 Subject: [PATCH 11/18] change FormatString::parse to only return the first error --- .../traits/on_unimplemented.rs | 35 +++++++---------- .../traits/on_unimplemented_format.rs | 33 +++++++--------- .../on_unimplemented/broken_format.rs | 2 - .../on_unimplemented/broken_format.stderr | 38 ++++++------------- 4 files changed, 40 insertions(+), 68 deletions(-) diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs index 37968386e9aeb..d1ab9e6a8c328 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs @@ -848,34 +848,27 @@ impl<'tcx> OnUnimplementedFormatString { } } } - // Errors from the underlying `rustc_parse_format::Parser` - Err(errors) => { + // Error from the underlying `rustc_parse_format::Parser` + Err(e) => { // we cannot return errors from processing the format string as hard error here // as the diagnostic namespace guarantees that malformed input cannot cause an error // // if we encounter any error while processing we nevertheless want to show it as warning // so that users are aware that something is not correct - for e in errors { - if self.is_diagnostic_namespace_variant { - if let Some(trait_def_id) = trait_def_id.as_local() { - tcx.emit_node_span_lint( - UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, - tcx.local_def_id_to_hir_id(trait_def_id), - self.span, - WrappedParserError { description: e.description, label: e.label }, - ); - } - } else { - let reported = struct_span_code_err!( - tcx.dcx(), + if self.is_diagnostic_namespace_variant { + if let Some(trait_def_id) = trait_def_id.as_local() { + tcx.emit_node_span_lint( + UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, + tcx.local_def_id_to_hir_id(trait_def_id), self.span, - E0231, - "{}", - e.description, - ) - .emit(); - result = Err(reported); + WrappedParserError { description: e.description, label: e.label }, + ); } + } else { + let reported = + struct_span_code_err!(tcx.dcx(), self.span, E0231, "{}", e.description,) + .emit(); + result = Err(reported); } } } diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_format.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_format.rs index 7c1dfc1728f04..720c6651b5baa 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_format.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_format.rs @@ -158,34 +158,29 @@ impl FormatString { self.span } - pub fn parse<'tcx>( - input: Symbol, - span: Span, - ctx: &Ctx<'tcx>, - ) -> Result> { + pub fn parse<'tcx>(input: Symbol, span: Span, ctx: &Ctx<'tcx>) -> Result { let s = input.as_str(); let mut parser = Parser::new(s, None, None, false, ParseMode::Format); - let mut pieces = Vec::new(); + let pieces: Vec<_> = parser.by_ref().collect(); + + if let Some(err) = parser.errors.into_iter().next() { + return Err(err); + } let mut warnings = Vec::new(); - for piece in &mut parser { - match piece { - RpfPiece::Lit(lit) => { - pieces.push(Piece::Lit(lit.into())); - } + let pieces = pieces + .into_iter() + .map(|piece| match piece { + RpfPiece::Lit(lit) => Piece::Lit(lit.into()), RpfPiece::NextArgument(arg) => { warn_on_format_spec(arg.format.clone(), &mut warnings, span); let arg = parse_arg(&arg, ctx, &mut warnings, span); - pieces.push(Piece::Arg(arg)); + Piece::Arg(arg) } - } - } + }) + .collect(); - if parser.errors.is_empty() { - Ok(FormatString { input, pieces, span, warnings }) - } else { - Err(parser.errors) - } + Ok(FormatString { input, pieces, span, warnings }) } pub fn format(&self, args: &FormatArgs<'_>) -> String { diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/broken_format.rs b/tests/ui/diagnostic_namespace/on_unimplemented/broken_format.rs index 44f269eb96706..62039e880a3d4 100644 --- a/tests/ui/diagnostic_namespace/on_unimplemented/broken_format.rs +++ b/tests/ui/diagnostic_namespace/on_unimplemented/broken_format.rs @@ -22,8 +22,6 @@ trait ImportantTrait4 {} #[diagnostic::on_unimplemented(message = "Test {Self:!}")] //~^WARN expected `}`, found `!` //~|WARN expected `}`, found `!` -//~|WARN unmatched `}` found -//~|WARN unmatched `}` found trait ImportantTrait5 {} fn check_1(_: impl ImportantTrait1) {} diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/broken_format.stderr b/tests/ui/diagnostic_namespace/on_unimplemented/broken_format.stderr index a82a1e78da0cf..db3476f92aad1 100644 --- a/tests/ui/diagnostic_namespace/on_unimplemented/broken_format.stderr +++ b/tests/ui/diagnostic_namespace/on_unimplemented/broken_format.stderr @@ -36,12 +36,6 @@ warning: expected `}`, found `!` LL | #[diagnostic::on_unimplemented(message = "Test {Self:!}")] | ^^^^^^^^^^^^^^^ -warning: unmatched `}` found - --> $DIR/broken_format.rs:22:42 - | -LL | #[diagnostic::on_unimplemented(message = "Test {Self:!}")] - | ^^^^^^^^^^^^^^^ - warning: unmatched `}` found --> $DIR/broken_format.rs:2:42 | @@ -51,7 +45,7 @@ LL | #[diagnostic::on_unimplemented(message = "{{Test } thing")] = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0277]: {{Test } thing - --> $DIR/broken_format.rs:36:13 + --> $DIR/broken_format.rs:34:13 | LL | check_1(()); | ------- ^^ the trait `ImportantTrait1` is not implemented for `()` @@ -64,7 +58,7 @@ help: this trait has no implementations, consider adding one LL | trait ImportantTrait1 {} | ^^^^^^^^^^^^^^^^^^^^^ note: required by a bound in `check_1` - --> $DIR/broken_format.rs:29:20 + --> $DIR/broken_format.rs:27:20 | LL | fn check_1(_: impl ImportantTrait1) {} | ^^^^^^^^^^^^^^^ required by this bound in `check_1` @@ -79,7 +73,7 @@ LL | #[diagnostic::on_unimplemented(message = "Test {}")] = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0277]: Test {} - --> $DIR/broken_format.rs:38:13 + --> $DIR/broken_format.rs:36:13 | LL | check_2(()); | ------- ^^ the trait `ImportantTrait2` is not implemented for `()` @@ -92,7 +86,7 @@ help: this trait has no implementations, consider adding one LL | trait ImportantTrait2 {} | ^^^^^^^^^^^^^^^^^^^^^ note: required by a bound in `check_2` - --> $DIR/broken_format.rs:30:20 + --> $DIR/broken_format.rs:28:20 | LL | fn check_2(_: impl ImportantTrait2) {} | ^^^^^^^^^^^^^^^ required by this bound in `check_2` @@ -107,7 +101,7 @@ LL | #[diagnostic::on_unimplemented(message = "Test {1:}")] = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0277]: Test {1} - --> $DIR/broken_format.rs:40:13 + --> $DIR/broken_format.rs:38:13 | LL | check_3(()); | ------- ^^ the trait `ImportantTrait3` is not implemented for `()` @@ -120,7 +114,7 @@ help: this trait has no implementations, consider adding one LL | trait ImportantTrait3 {} | ^^^^^^^^^^^^^^^^^^^^^ note: required by a bound in `check_3` - --> $DIR/broken_format.rs:31:20 + --> $DIR/broken_format.rs:29:20 | LL | fn check_3(_: impl ImportantTrait3) {} | ^^^^^^^^^^^^^^^ required by this bound in `check_3` @@ -135,7 +129,7 @@ LL | #[diagnostic::on_unimplemented(message = "Test {Self:123}")] = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0277]: Test () - --> $DIR/broken_format.rs:42:13 + --> $DIR/broken_format.rs:40:13 | LL | check_4(()); | ------- ^^ the trait `ImportantTrait4` is not implemented for `()` @@ -148,7 +142,7 @@ help: this trait has no implementations, consider adding one LL | trait ImportantTrait4 {} | ^^^^^^^^^^^^^^^^^^^^^ note: required by a bound in `check_4` - --> $DIR/broken_format.rs:32:20 + --> $DIR/broken_format.rs:30:20 | LL | fn check_4(_: impl ImportantTrait4) {} | ^^^^^^^^^^^^^^^ required by this bound in `check_4` @@ -161,16 +155,8 @@ LL | #[diagnostic::on_unimplemented(message = "Test {Self:!}")] | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -warning: unmatched `}` found - --> $DIR/broken_format.rs:22:42 - | -LL | #[diagnostic::on_unimplemented(message = "Test {Self:!}")] - | ^^^^^^^^^^^^^^^ - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - error[E0277]: Test {Self:!} - --> $DIR/broken_format.rs:44:13 + --> $DIR/broken_format.rs:42:13 | LL | check_5(()); | ------- ^^ the trait `ImportantTrait5` is not implemented for `()` @@ -178,16 +164,16 @@ LL | check_5(()); | required by a bound introduced by this call | help: this trait has no implementations, consider adding one - --> $DIR/broken_format.rs:27:1 + --> $DIR/broken_format.rs:25:1 | LL | trait ImportantTrait5 {} | ^^^^^^^^^^^^^^^^^^^^^ note: required by a bound in `check_5` - --> $DIR/broken_format.rs:33:20 + --> $DIR/broken_format.rs:31:20 | LL | fn check_5(_: impl ImportantTrait5) {} | ^^^^^^^^^^^^^^^ required by this bound in `check_5` -error: aborting due to 5 previous errors; 12 warnings emitted +error: aborting due to 5 previous errors; 10 warnings emitted For more information about this error, try `rustc --explain E0277`. From c7174a761b7f5b70a1abd1b2a122876ffe097c0f Mon Sep 17 00:00:00 2001 From: mejrs <59372212+mejrs@users.noreply.github.com> Date: Fri, 23 May 2025 23:49:29 +0200 Subject: [PATCH 12/18] rename Parser's lifetime to `'input` --- compiler/rustc_parse_format/src/lib.rs | 58 +++++++++++++------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/compiler/rustc_parse_format/src/lib.rs b/compiler/rustc_parse_format/src/lib.rs index 6e99c75324eda..d17e0677f8a10 100644 --- a/compiler/rustc_parse_format/src/lib.rs +++ b/compiler/rustc_parse_format/src/lib.rs @@ -34,27 +34,27 @@ pub enum ParseMode { /// A piece is a portion of the format string which represents the next part /// to emit. These are emitted as a stream by the `Parser` class. #[derive(Clone, Debug, PartialEq)] -pub enum Piece<'a> { +pub enum Piece<'input> { /// A literal string which should directly be emitted - Lit(&'a str), + Lit(&'input str), /// This describes that formatting should process the next argument (as /// specified inside) for emission. - NextArgument(Box>), + NextArgument(Box>), } /// Representation of an argument specification. #[derive(Clone, Debug, PartialEq)] -pub struct Argument<'a> { +pub struct Argument<'input> { /// Where to find this argument - pub position: Position<'a>, + pub position: Position<'input>, /// The span of the position indicator. Includes any whitespace in implicit /// positions (`{ }`). pub position_span: Range, /// How to format the argument - pub format: FormatSpec<'a>, + pub format: FormatSpec<'input>, } -impl<'a> Argument<'a> { +impl<'input> Argument<'input> { pub fn is_identifier(&self) -> bool { matches!(self.position, Position::ArgumentNamed(_)) && self.format == FormatSpec::default() } @@ -62,7 +62,7 @@ impl<'a> Argument<'a> { /// Specification for the formatting of an argument in the format string. #[derive(Clone, Debug, PartialEq, Default)] -pub struct FormatSpec<'a> { +pub struct FormatSpec<'input> { /// Optionally specified character to fill alignment with. pub fill: Option, /// Span of the optionally specified fill character. @@ -78,30 +78,30 @@ pub struct FormatSpec<'a> { /// The `x` or `X` flag. (Only for `Debug`.) pub debug_hex: Option, /// The integer precision to use. - pub precision: Count<'a>, + pub precision: Count<'input>, /// The span of the precision formatting flag (for diagnostics). pub precision_span: Option>, /// The string width requested for the resulting format. - pub width: Count<'a>, + pub width: Count<'input>, /// The span of the width formatting flag (for diagnostics). pub width_span: Option>, /// The descriptor string representing the name of the format desired for /// this argument, this can be empty or any number of characters, although /// it is required to be one word. - pub ty: &'a str, + pub ty: &'input str, /// The span of the descriptor string (for diagnostics). pub ty_span: Option>, } /// Enum describing where an argument for a format can be located. #[derive(Clone, Debug, PartialEq)] -pub enum Position<'a> { +pub enum Position<'input> { /// The argument is implied to be located at an index ArgumentImplicitlyIs(usize), /// The argument is located at a specific index given in the format, ArgumentIs(usize), /// The argument has a name. - ArgumentNamed(&'a str), + ArgumentNamed(&'input str), } impl Position<'_> { @@ -148,11 +148,11 @@ pub enum DebugHex { /// A count is used for the precision and width parameters of an integer, and /// can reference either an argument or a literal integer. #[derive(Clone, Debug, PartialEq, Default)] -pub enum Count<'a> { +pub enum Count<'input> { /// The count is specified explicitly. CountIs(u16), /// The count is specified by the argument with the given name. - CountIsName(&'a str, Range), + CountIsName(&'input str, Range), /// The count is specified by the argument at the given index. CountIsParam(usize), /// The count is specified by a star (like in `{:.*}`) that refers to the argument at the given index. @@ -192,10 +192,10 @@ pub enum Suggestion { /// /// This is a recursive-descent parser for the sake of simplicity, and if /// necessary there's probably lots of room for improvement performance-wise. -pub struct Parser<'a> { +pub struct Parser<'input> { mode: ParseMode, /// Input to be parsed - input: &'a str, + input: &'input str, /// Tuples of the span in the code snippet (input as written before being unescaped), the pos in input, and the char in input input_vec: Vec<(Range, usize, char)>, /// Index into input_vec @@ -221,10 +221,10 @@ pub struct Parser<'a> { pub line_spans: Vec>, } -impl<'a> Iterator for Parser<'a> { - type Item = Piece<'a>; +impl<'input> Iterator for Parser<'input> { + type Item = Piece<'input>; - fn next(&mut self) -> Option> { + fn next(&mut self) -> Option> { if let Some((Range { start, end }, idx, ch)) = self.peek() { match ch { '{' => { @@ -287,14 +287,14 @@ impl<'a> Iterator for Parser<'a> { } } -impl<'a> Parser<'a> { +impl<'input> Parser<'input> { /// Creates a new parser for the given unescaped input string and /// optional code snippet (the input as written before being unescaped), /// where `style` is `Some(nr_hashes)` when the snippet is a raw string with that many hashes. /// If the input comes via `println` or `panic`, then it has a newline already appended, /// which is reflected in the `appended_newline` parameter. pub fn new( - input: &'a str, + input: &'input str, style: Option, snippet: Option, appended_newline: bool, @@ -471,7 +471,7 @@ impl<'a> Parser<'a> { /// Parses all of a string which is to be considered a "raw literal" in a /// format string. This is everything outside of the braces. - fn string(&mut self, start: usize) -> &'a str { + fn string(&mut self, start: usize) -> &'input str { while let Some((r, i, c)) = self.peek() { match c { '{' | '}' => { @@ -495,7 +495,7 @@ impl<'a> Parser<'a> { } /// Parses an `Argument` structure, or what's contained within braces inside the format string. - fn argument(&mut self) -> Argument<'a> { + fn argument(&mut self) -> Argument<'input> { let start_idx = self.input_vec_index; let position = self.position(); @@ -524,7 +524,7 @@ impl<'a> Parser<'a> { /// integer index of an argument, a named argument, or a blank string. /// Returns `Some(parsed_position)` if the position is not implicitly /// consuming a macro argument, `None` if it's the case. - fn position(&mut self) -> Option> { + fn position(&mut self) -> Option> { if let Some(i) = self.integer() { Some(ArgumentIs(i.into())) } else { @@ -579,7 +579,7 @@ impl<'a> Parser<'a> { /// Parses a format specifier at the current position, returning all of the /// relevant information in the `FormatSpec` struct. - fn format(&mut self) -> FormatSpec<'a> { + fn format(&mut self) -> FormatSpec<'input> { let mut spec = FormatSpec::default(); if !self.consume(':') { @@ -697,7 +697,7 @@ impl<'a> Parser<'a> { /// Parses an inline assembly template modifier at the current position, returning the modifier /// in the `ty` field of the `FormatSpec` struct. - fn inline_asm(&mut self) -> FormatSpec<'a> { + fn inline_asm(&mut self) -> FormatSpec<'input> { let mut spec = FormatSpec::default(); if !self.consume(':') { @@ -718,7 +718,7 @@ impl<'a> Parser<'a> { /// Parses a `Count` parameter at the current position. This does not check /// for 'CountIsNextParam' because that is only used in precision, not /// width. - fn count(&mut self) -> Count<'a> { + fn count(&mut self) -> Count<'input> { if let Some(i) = self.integer() { if self.consume('$') { CountIsParam(i.into()) } else { CountIs(i) } } else { @@ -737,7 +737,7 @@ impl<'a> Parser<'a> { /// Parses a word starting at the current position. A word is the same as a /// Rust identifier, except that it can't start with `_` character. - fn word(&mut self) -> &'a str { + fn word(&mut self) -> &'input str { let index = self.input_vec_index; match self.peek() { Some((ref r, i, c)) if rustc_lexer::is_id_start(c) => { From 03c846ee1d118d8a2a39085c7365655706aee0f8 Mon Sep 17 00:00:00 2001 From: mejrs <59372212+mejrs@users.noreply.github.com> Date: Sat, 24 May 2025 18:12:18 +0200 Subject: [PATCH 13/18] Introduce ParseMode::diagnostic and fix multiline spans --- compiler/rustc_parse_format/src/lib.rs | 22 ++++ .../traits/on_unimplemented.rs | 6 +- .../traits/on_unimplemented_condition.rs | 2 +- .../traits/on_unimplemented_format.rs | 64 +++++------ .../diagnostic_namespace/multiline_spans.rs | 10 +- .../multiline_spans.stderr | 56 +++++---- .../on_unimplemented/broken_format.rs | 17 ++- .../on_unimplemented/broken_format.stderr | 106 +++++++++++++----- 8 files changed, 188 insertions(+), 95 deletions(-) diff --git a/compiler/rustc_parse_format/src/lib.rs b/compiler/rustc_parse_format/src/lib.rs index d17e0677f8a10..42bd0f5d847f7 100644 --- a/compiler/rustc_parse_format/src/lib.rs +++ b/compiler/rustc_parse_format/src/lib.rs @@ -29,6 +29,11 @@ pub enum ParseMode { Format, /// An inline assembly template string for `asm!`. InlineAsm, + /// A format string for use in diagnostic attributes. + /// + /// Similar to `format_args!`, however only named ("captured") arguments + /// are allowed, and no format modifiers are permitted. + Diagnostic, } /// A piece is a portion of the format string which represents the next part @@ -506,6 +511,7 @@ impl<'input> Parser<'input> { let format = match self.mode { ParseMode::Format => self.format(), ParseMode::InlineAsm => self.inline_asm(), + ParseMode::Diagnostic => self.diagnostic(), }; // Resolve position after parsing format spec. @@ -715,6 +721,22 @@ impl<'input> Parser<'input> { spec } + /// Always returns an empty `FormatSpec` + fn diagnostic(&mut self) -> FormatSpec<'input> { + let mut spec = FormatSpec::default(); + + let Some((Range { start, .. }, start_idx)) = self.consume_pos(':') else { + return spec; + }; + + spec.ty = self.string(start_idx); + spec.ty_span = { + let end = self.input_vec_index2range(self.input_vec_index).start; + Some(start..end) + }; + spec + } + /// Parses a `Count` parameter at the current position. This does not check /// for 'CountIsNextParam' because that is only used in precision, not /// width. diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs index d1ab9e6a8c328..89dab90dc681c 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs @@ -810,7 +810,8 @@ impl<'tcx> OnUnimplementedFormatString { let mut result = Ok(()); - match FormatString::parse(self.symbol, self.span, &ctx) { + let snippet = tcx.sess.source_map().span_to_snippet(self.span).ok(); + match FormatString::parse(self.symbol, snippet, self.span, &ctx) { // Warnings about format specifiers, deprecated parameters, wrong parameters etc. // In other words we'd like to let the author know, but we can still try to format the string later Ok(FormatString { warnings, .. }) => { @@ -889,7 +890,8 @@ impl<'tcx> OnUnimplementedFormatString { Ctx::RustcOnUnimplemented { tcx, trait_def_id } }; - if let Ok(s) = FormatString::parse(self.symbol, self.span, &ctx) { + // No point passing a snippet here, we already did that in `verify` + if let Ok(s) = FormatString::parse(self.symbol, None, self.span, &ctx) { s.format(args) } else { // we cannot return errors from processing the format string as hard error here diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_condition.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_condition.rs index e8ea9f2d23ebd..171d05230d468 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_condition.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_condition.rs @@ -198,7 +198,7 @@ enum LitOrArg { impl FilterFormatString { fn parse(input: Symbol) -> Self { - let pieces = Parser::new(input.as_str(), None, None, false, ParseMode::Format) + let pieces = Parser::new(input.as_str(), None, None, false, ParseMode::Diagnostic) .map(|p| match p { Piece::Lit(s) => LitOrArg::Lit(s.to_owned()), // We just ignore formatspecs here diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_format.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_format.rs index 720c6651b5baa..3e8b906fa93d2 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_format.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_format.rs @@ -5,12 +5,11 @@ use errors::*; use rustc_middle::ty::print::TraitRefPrintSugared; use rustc_middle::ty::{GenericParamDefKind, TyCtxt}; use rustc_parse_format::{ - Alignment, Argument, Count, FormatSpec, ParseError, ParseMode, Parser, Piece as RpfPiece, - Position, + Argument, FormatSpec, ParseError, ParseMode, Parser, Piece as RpfPiece, Position, }; use rustc_session::lint::builtin::UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES; use rustc_span::def_id::DefId; -use rustc_span::{BytePos, Pos, Span, Symbol, kw, sym}; +use rustc_span::{InnerSpan, Span, Symbol, kw, sym}; /// Like [std::fmt::Arguments] this is a string that has been parsed into "pieces", /// either as string pieces or dynamic arguments. @@ -158,9 +157,14 @@ impl FormatString { self.span } - pub fn parse<'tcx>(input: Symbol, span: Span, ctx: &Ctx<'tcx>) -> Result { + pub fn parse<'tcx>( + input: Symbol, + snippet: Option, + span: Span, + ctx: &Ctx<'tcx>, + ) -> Result { let s = input.as_str(); - let mut parser = Parser::new(s, None, None, false, ParseMode::Format); + let mut parser = Parser::new(s, None, snippet, false, ParseMode::Diagnostic); let pieces: Vec<_> = parser.by_ref().collect(); if let Some(err) = parser.errors.into_iter().next() { @@ -173,8 +177,8 @@ impl FormatString { .map(|piece| match piece { RpfPiece::Lit(lit) => Piece::Lit(lit.into()), RpfPiece::NextArgument(arg) => { - warn_on_format_spec(arg.format.clone(), &mut warnings, span); - let arg = parse_arg(&arg, ctx, &mut warnings, span); + warn_on_format_spec(&arg.format, &mut warnings, span, parser.is_source_literal); + let arg = parse_arg(&arg, ctx, &mut warnings, span, parser.is_source_literal); Piece::Arg(arg) } }) @@ -224,11 +228,12 @@ fn parse_arg<'tcx>( ctx: &Ctx<'tcx>, warnings: &mut Vec, input_span: Span, + is_source_literal: bool, ) -> FormatArg { let (Ctx::RustcOnUnimplemented { tcx, trait_def_id } | Ctx::DiagnosticOnUnimplemented { tcx, trait_def_id }) = ctx; - let span = slice_span(input_span, arg.position_span.clone()); + let span = slice_span(input_span, arg.position_span.clone(), is_source_literal); match arg.position { // Something like "hello {name}" @@ -278,39 +283,24 @@ fn parse_arg<'tcx>( /// `#[rustc_on_unimplemented]` and `#[diagnostic::...]` don't actually do anything /// with specifiers, so emit a warning if they are used. -fn warn_on_format_spec(spec: FormatSpec<'_>, warnings: &mut Vec, input_span: Span) { - if !matches!( - spec, - FormatSpec { - fill: None, - fill_span: None, - align: Alignment::AlignUnknown, - sign: None, - alternate: false, - zero_pad: false, - debug_hex: None, - precision: Count::CountImplied, - precision_span: None, - width: Count::CountImplied, - width_span: None, - ty: _, - ty_span: _, - }, - ) { - let span = spec.ty_span.map(|inner| slice_span(input_span, inner)).unwrap_or(input_span); +fn warn_on_format_spec( + spec: &FormatSpec<'_>, + warnings: &mut Vec, + input_span: Span, + is_source_literal: bool, +) { + if spec.ty != "" { + let span = spec + .ty_span + .as_ref() + .map(|inner| slice_span(input_span, inner.clone(), is_source_literal)) + .unwrap_or(input_span); warnings.push(FormatWarning::InvalidSpecifier { span, name: spec.ty.into() }) } } -fn slice_span(input: Span, range: Range) -> Span { - let span = input.data(); - - Span::new( - span.lo + BytePos::from_usize(range.start), - span.lo + BytePos::from_usize(range.end), - span.ctxt, - span.parent, - ) +fn slice_span(input: Span, Range { start, end }: Range, is_source_literal: bool) -> Span { + if is_source_literal { input.from_inner(InnerSpan { start, end }) } else { input } } pub mod errors { diff --git a/tests/ui/diagnostic_namespace/multiline_spans.rs b/tests/ui/diagnostic_namespace/multiline_spans.rs index 97dd07b2bf365..994dd9fd011c3 100644 --- a/tests/ui/diagnostic_namespace/multiline_spans.rs +++ b/tests/ui/diagnostic_namespace/multiline_spans.rs @@ -5,7 +5,7 @@ #[diagnostic::on_unimplemented(message = "here is a big \ multiline string \ {unknown}")] -//~^^ ERROR there is no parameter `unknown` on trait `MultiLine` [unknown_or_malformed_diagnostic_attributes] +//~^ ERROR there is no parameter `unknown` on trait `MultiLine` [unknown_or_malformed_diagnostic_attributes] pub trait MultiLine {} #[diagnostic::on_unimplemented(message = "here is a big \ @@ -25,22 +25,23 @@ pub trait MultiLine3 {} \ \ multiline string {unknown}")] -//~^^^^ ERROR there is no parameter `unknown` on trait `MultiLine4` [unknown_or_malformed_diagnostic_attributes] +//~^ ERROR there is no parameter `unknown` on trait `MultiLine4` [unknown_or_malformed_diagnostic_attributes] pub trait MultiLine4 {} #[diagnostic::on_unimplemented(message = "here is a big \ multiline string \ {Self:+}")] -//~^^^ ERROR invalid format specifier [unknown_or_malformed_diagnostic_attributes] +//~^ ERROR invalid format specifier [unknown_or_malformed_diagnostic_attributes] pub trait MultiLineFmt {} #[diagnostic::on_unimplemented(message = "here is a big \ multiline string {Self:X}")] +//~^ ERROR invalid format specifier [unknown_or_malformed_diagnostic_attributes] pub trait MultiLineFmt2 {} #[diagnostic::on_unimplemented(message = "here is a big \ multiline string {Self:#}")] -//~^^ ERROR invalid format specifier [unknown_or_malformed_diagnostic_attributes] +//~^ ERROR invalid format specifier [unknown_or_malformed_diagnostic_attributes] pub trait MultiLineFmt3 {} @@ -50,4 +51,5 @@ pub trait MultiLineFmt3 {} \ \ multiline string {Self:?}")] +//~^ ERROR invalid format specifier [unknown_or_malformed_diagnostic_attributes] pub trait MultiLineFmt4 {} diff --git a/tests/ui/diagnostic_namespace/multiline_spans.stderr b/tests/ui/diagnostic_namespace/multiline_spans.stderr index 8d86560b66c10..894bfe3d90a50 100644 --- a/tests/ui/diagnostic_namespace/multiline_spans.stderr +++ b/tests/ui/diagnostic_namespace/multiline_spans.stderr @@ -1,8 +1,8 @@ error: there is no parameter `unknown` on trait `MultiLine` - --> $DIR/multiline_spans.rs:6:17 + --> $DIR/multiline_spans.rs:7:43 | -LL | ... multiline string \ - | ^^^^^^^ +LL | ... {unknown}")] + | ^^^^^^^ | = help: expect either a generic argument name or `{Self}` as format argument note: the lint level is defined here @@ -12,50 +12,60 @@ LL | #![deny(unknown_or_malformed_diagnostic_attributes)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: there is no parameter `unknown` on trait `MultiLine2` - --> $DIR/multiline_spans.rs:12:17 + --> $DIR/multiline_spans.rs:12:60 | LL | ... multiline string {unknown}")] - | ^^^^^^^ + | ^^^^^^^ | = help: expect either a generic argument name or `{Self}` as format argument error: there is no parameter `unknown` on trait `MultiLine3` - --> $DIR/multiline_spans.rs:17:17 + --> $DIR/multiline_spans.rs:17:23 | LL | multiline string {unknown}")] - | ^^^^^^^ + | ^^^^^^^ | = help: expect either a generic argument name or `{Self}` as format argument error: there is no parameter `unknown` on trait `MultiLine4` - --> $DIR/multiline_spans.rs:24:15 + --> $DIR/multiline_spans.rs:27:23 | -LL | / \ -LL | | \ - | |___^ +LL | multiline string {unknown}")] + | ^^^^^^^ | = help: expect either a generic argument name or `{Self}` as format argument error: invalid format specifier - --> $DIR/multiline_spans.rs:31:42 + --> $DIR/multiline_spans.rs:33:47 + | +LL | ... {Self:+}")] + | ^^ + | + = help: no format specifier are supported in this position + +error: invalid format specifier + --> $DIR/multiline_spans.rs:38:64 + | +LL | ... multiline string {Self:X}")] + | ^^ + | + = help: no format specifier are supported in this position + +error: invalid format specifier + --> $DIR/multiline_spans.rs:43:27 | -LL | #[diagnostic::on_unimplemented(message = "here is a big \ - | __________________________________________^ -LL | | multiline string \ -LL | | {Self:+}")] - | |__________________________________________________^ +LL | multiline string {Self:#}")] + | ^^ | = help: no format specifier are supported in this position error: invalid format specifier - --> $DIR/multiline_spans.rs:41:42 + --> $DIR/multiline_spans.rs:53:27 | -LL | #[diagnostic::on_unimplemented(message = "here is a big \ - | __________________________________________^ -LL | | multiline string {Self:#}")] - | |______________________________^ +LL | multiline string {Self:?}")] + | ^^ | = help: no format specifier are supported in this position -error: aborting due to 6 previous errors +error: aborting due to 8 previous errors diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/broken_format.rs b/tests/ui/diagnostic_namespace/on_unimplemented/broken_format.rs index 62039e880a3d4..4762d9e793faf 100644 --- a/tests/ui/diagnostic_namespace/on_unimplemented/broken_format.rs +++ b/tests/ui/diagnostic_namespace/on_unimplemented/broken_format.rs @@ -12,6 +12,8 @@ trait ImportantTrait2 {} #[diagnostic::on_unimplemented(message = "Test {1:}")] //~^WARN positional format arguments are not allowed here //~|WARN positional format arguments are not allowed here +//~|WARN invalid format specifier [unknown_or_malformed_diagnostic_attributes] +//~|WARN invalid format specifier [unknown_or_malformed_diagnostic_attributes] trait ImportantTrait3 {} #[diagnostic::on_unimplemented(message = "Test {Self:123}")] @@ -20,15 +22,22 @@ trait ImportantTrait3 {} trait ImportantTrait4 {} #[diagnostic::on_unimplemented(message = "Test {Self:!}")] -//~^WARN expected `}`, found `!` -//~|WARN expected `}`, found `!` +//~^WARN invalid format specifier [unknown_or_malformed_diagnostic_attributes] +//~|WARN invalid format specifier [unknown_or_malformed_diagnostic_attributes] trait ImportantTrait5 {} +#[diagnostic::on_unimplemented(message = "Test {Self:}")] +//~^WARN invalid format specifier [unknown_or_malformed_diagnostic_attributes] +//~|WARN invalid format specifier [unknown_or_malformed_diagnostic_attributes] +trait ImportantTrait6 {} + + fn check_1(_: impl ImportantTrait1) {} fn check_2(_: impl ImportantTrait2) {} fn check_3(_: impl ImportantTrait3) {} fn check_4(_: impl ImportantTrait4) {} fn check_5(_: impl ImportantTrait5) {} +fn check_6(_: impl ImportantTrait6) {} fn main() { check_1(()); @@ -40,5 +49,7 @@ fn main() { check_4(()); //~^ERROR Test () check_5(()); - //~^ERROR Test {Self:!} + //~^ERROR Test () + check_6(()); + //~^ERROR Test () } diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/broken_format.stderr b/tests/ui/diagnostic_namespace/on_unimplemented/broken_format.stderr index db3476f92aad1..2670d0630f7f6 100644 --- a/tests/ui/diagnostic_namespace/on_unimplemented/broken_format.stderr +++ b/tests/ui/diagnostic_namespace/on_unimplemented/broken_format.stderr @@ -14,6 +14,14 @@ LL | #[diagnostic::on_unimplemented(message = "Test {}")] | = help: only named format arguments with the name of one of the generic types are allowed in this context +warning: invalid format specifier + --> $DIR/broken_format.rs:12:50 + | +LL | #[diagnostic::on_unimplemented(message = "Test {1:}")] + | ^ + | + = help: no format specifier are supported in this position + warning: positional format arguments are not allowed here --> $DIR/broken_format.rs:12:49 | @@ -23,18 +31,28 @@ LL | #[diagnostic::on_unimplemented(message = "Test {1:}")] = help: only named format arguments with the name of one of the generic types are allowed in this context warning: invalid format specifier - --> $DIR/broken_format.rs:17:42 + --> $DIR/broken_format.rs:19:53 | LL | #[diagnostic::on_unimplemented(message = "Test {Self:123}")] - | ^^^^^^^^^^^^^^^^^ + | ^^^^ | = help: no format specifier are supported in this position -warning: expected `}`, found `!` - --> $DIR/broken_format.rs:22:42 +warning: invalid format specifier + --> $DIR/broken_format.rs:24:53 | LL | #[diagnostic::on_unimplemented(message = "Test {Self:!}")] - | ^^^^^^^^^^^^^^^ + | ^^ + | + = help: no format specifier are supported in this position + +warning: invalid format specifier + --> $DIR/broken_format.rs:29:53 + | +LL | #[diagnostic::on_unimplemented(message = "Test {Self:}")] + | ^ + | + = help: no format specifier are supported in this position warning: unmatched `}` found --> $DIR/broken_format.rs:2:42 @@ -45,7 +63,7 @@ LL | #[diagnostic::on_unimplemented(message = "{{Test } thing")] = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0277]: {{Test } thing - --> $DIR/broken_format.rs:34:13 + --> $DIR/broken_format.rs:43:13 | LL | check_1(()); | ------- ^^ the trait `ImportantTrait1` is not implemented for `()` @@ -58,7 +76,7 @@ help: this trait has no implementations, consider adding one LL | trait ImportantTrait1 {} | ^^^^^^^^^^^^^^^^^^^^^ note: required by a bound in `check_1` - --> $DIR/broken_format.rs:27:20 + --> $DIR/broken_format.rs:35:20 | LL | fn check_1(_: impl ImportantTrait1) {} | ^^^^^^^^^^^^^^^ required by this bound in `check_1` @@ -73,7 +91,7 @@ LL | #[diagnostic::on_unimplemented(message = "Test {}")] = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0277]: Test {} - --> $DIR/broken_format.rs:36:13 + --> $DIR/broken_format.rs:45:13 | LL | check_2(()); | ------- ^^ the trait `ImportantTrait2` is not implemented for `()` @@ -86,11 +104,20 @@ help: this trait has no implementations, consider adding one LL | trait ImportantTrait2 {} | ^^^^^^^^^^^^^^^^^^^^^ note: required by a bound in `check_2` - --> $DIR/broken_format.rs:28:20 + --> $DIR/broken_format.rs:36:20 | LL | fn check_2(_: impl ImportantTrait2) {} | ^^^^^^^^^^^^^^^ required by this bound in `check_2` +warning: invalid format specifier + --> $DIR/broken_format.rs:12:50 + | +LL | #[diagnostic::on_unimplemented(message = "Test {1:}")] + | ^ + | + = help: no format specifier are supported in this position + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + warning: positional format arguments are not allowed here --> $DIR/broken_format.rs:12:49 | @@ -101,7 +128,7 @@ LL | #[diagnostic::on_unimplemented(message = "Test {1:}")] = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0277]: Test {1} - --> $DIR/broken_format.rs:38:13 + --> $DIR/broken_format.rs:47:13 | LL | check_3(()); | ------- ^^ the trait `ImportantTrait3` is not implemented for `()` @@ -109,27 +136,27 @@ LL | check_3(()); | required by a bound introduced by this call | help: this trait has no implementations, consider adding one - --> $DIR/broken_format.rs:15:1 + --> $DIR/broken_format.rs:17:1 | LL | trait ImportantTrait3 {} | ^^^^^^^^^^^^^^^^^^^^^ note: required by a bound in `check_3` - --> $DIR/broken_format.rs:29:20 + --> $DIR/broken_format.rs:37:20 | LL | fn check_3(_: impl ImportantTrait3) {} | ^^^^^^^^^^^^^^^ required by this bound in `check_3` warning: invalid format specifier - --> $DIR/broken_format.rs:17:42 + --> $DIR/broken_format.rs:19:53 | LL | #[diagnostic::on_unimplemented(message = "Test {Self:123}")] - | ^^^^^^^^^^^^^^^^^ + | ^^^^ | = help: no format specifier are supported in this position = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0277]: Test () - --> $DIR/broken_format.rs:40:13 + --> $DIR/broken_format.rs:49:13 | LL | check_4(()); | ------- ^^ the trait `ImportantTrait4` is not implemented for `()` @@ -137,26 +164,27 @@ LL | check_4(()); | required by a bound introduced by this call | help: this trait has no implementations, consider adding one - --> $DIR/broken_format.rs:20:1 + --> $DIR/broken_format.rs:22:1 | LL | trait ImportantTrait4 {} | ^^^^^^^^^^^^^^^^^^^^^ note: required by a bound in `check_4` - --> $DIR/broken_format.rs:30:20 + --> $DIR/broken_format.rs:38:20 | LL | fn check_4(_: impl ImportantTrait4) {} | ^^^^^^^^^^^^^^^ required by this bound in `check_4` -warning: expected `}`, found `!` - --> $DIR/broken_format.rs:22:42 +warning: invalid format specifier + --> $DIR/broken_format.rs:24:53 | LL | #[diagnostic::on_unimplemented(message = "Test {Self:!}")] - | ^^^^^^^^^^^^^^^ + | ^^ | + = help: no format specifier are supported in this position = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error[E0277]: Test {Self:!} - --> $DIR/broken_format.rs:42:13 +error[E0277]: Test () + --> $DIR/broken_format.rs:51:13 | LL | check_5(()); | ------- ^^ the trait `ImportantTrait5` is not implemented for `()` @@ -164,16 +192,44 @@ LL | check_5(()); | required by a bound introduced by this call | help: this trait has no implementations, consider adding one - --> $DIR/broken_format.rs:25:1 + --> $DIR/broken_format.rs:27:1 | LL | trait ImportantTrait5 {} | ^^^^^^^^^^^^^^^^^^^^^ note: required by a bound in `check_5` - --> $DIR/broken_format.rs:31:20 + --> $DIR/broken_format.rs:39:20 | LL | fn check_5(_: impl ImportantTrait5) {} | ^^^^^^^^^^^^^^^ required by this bound in `check_5` -error: aborting due to 5 previous errors; 10 warnings emitted +warning: invalid format specifier + --> $DIR/broken_format.rs:29:53 + | +LL | #[diagnostic::on_unimplemented(message = "Test {Self:}")] + | ^ + | + = help: no format specifier are supported in this position + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0277]: Test () + --> $DIR/broken_format.rs:53:13 + | +LL | check_6(()); + | ------- ^^ the trait `ImportantTrait6` is not implemented for `()` + | | + | required by a bound introduced by this call + | +help: this trait has no implementations, consider adding one + --> $DIR/broken_format.rs:32:1 + | +LL | trait ImportantTrait6 {} + | ^^^^^^^^^^^^^^^^^^^^^ +note: required by a bound in `check_6` + --> $DIR/broken_format.rs:40:20 + | +LL | fn check_6(_: impl ImportantTrait6) {} + | ^^^^^^^^^^^^^^^ required by this bound in `check_6` + +error: aborting due to 6 previous errors; 14 warnings emitted For more information about this error, try `rustc --explain E0277`. From ba1c650a2cf3f72cd7c1b55ccf191e56bff3f47d Mon Sep 17 00:00:00 2001 From: mejrs <59372212+mejrs@users.noreply.github.com> Date: Mon, 9 Jun 2025 16:23:55 +0200 Subject: [PATCH 14/18] Add ParseMode::Diagnostic unit tests --- compiler/rustc_parse_format/src/tests.rs | 42 ++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/compiler/rustc_parse_format/src/tests.rs b/compiler/rustc_parse_format/src/tests.rs index e6a7f24034ad7..a6c7e1890abd1 100644 --- a/compiler/rustc_parse_format/src/tests.rs +++ b/compiler/rustc_parse_format/src/tests.rs @@ -553,3 +553,45 @@ fn asm_concat() { assert_eq!(parser.by_ref().collect::>>(), &[Lit(asm)]); assert_eq!(parser.line_spans, &[]); } + +#[test] +fn diagnostic_format_flags() { + let lit = "{thing:blah}"; + let mut parser = Parser::new(lit, None, None, false, ParseMode::Diagnostic); + assert!(!parser.is_source_literal); + + let [NextArgument(arg)] = &*parser.by_ref().collect::>>() else { panic!() }; + + assert_eq!( + **arg, + Argument { + position: ArgumentNamed("thing"), + position_span: 2..7, + format: FormatSpec { ty: ":blah", ty_span: Some(7..12), ..Default::default() }, + } + ); + + assert_eq!(parser.line_spans, &[]); + assert!(parser.errors.is_empty()); +} + +#[test] +fn diagnostic_format_mod() { + let lit = "{thing:+}"; + let mut parser = Parser::new(lit, None, None, false, ParseMode::Diagnostic); + assert!(!parser.is_source_literal); + + let [NextArgument(arg)] = &*parser.by_ref().collect::>>() else { panic!() }; + + assert_eq!( + **arg, + Argument { + position: ArgumentNamed("thing"), + position_span: 2..7, + format: FormatSpec { ty: ":+", ty_span: Some(7..9), ..Default::default() }, + } + ); + + assert_eq!(parser.line_spans, &[]); + assert!(parser.errors.is_empty()); +} From 2095211fb674df018ddd37d37677a1d2eaf61e49 Mon Sep 17 00:00:00 2001 From: Gray Olson Date: Tue, 10 Jun 2025 16:59:21 +0200 Subject: [PATCH 15/18] core docs: improve clarity of considerations about atomic CAS operations - Rewords existing Considerations section on `fetch_update` and friends to make clear that the limitations are inherent to an implementation based on any CAS operation, rather than the weak version of `compare_exchange` in particular - Add Considerations to `compare_exchange` and `compare_exchange_weak` which details similar considerations and when they may be relevant. --- library/core/src/sync/atomic.rs | 179 ++++++++++++++++++++++++++------ 1 file changed, 146 insertions(+), 33 deletions(-) diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs index 4f9f2936564ff..04c8d1473b048 100644 --- a/library/core/src/sync/atomic.rs +++ b/library/core/src/sync/atomic.rs @@ -891,6 +891,19 @@ impl AtomicBool { /// Err(false)); /// assert_eq!(some_bool.load(Ordering::Relaxed), false); /// ``` + /// + /// # Considerations + /// + /// `compare_exchange` is a [compare-and-swap operation] and thus exhibits the usual downsides + /// of CAS operations. In particular, a load of the value followed by a successful + /// `compare_exchange` with the previous load *does not ensure* that other threads have not + /// changed the value in the interim. This is usually important when the *equality* check in + /// the `compare_exchange` is being used to check the *identity* of a value, but equality + /// does not necessarily imply identity. In this case, `compare_exchange` can lead to the + /// [ABA problem]. + /// + /// [ABA Problem]: https://en.wikipedia.org/wiki/ABA_problem + /// [compare-and-swap operation]: https://en.wikipedia.org/wiki/Compare-and-swap #[inline] #[stable(feature = "extended_compare_and_swap", since = "1.10.0")] #[doc(alias = "compare_and_swap")] @@ -973,6 +986,19 @@ impl AtomicBool { /// } /// } /// ``` + /// + /// # Considerations + /// + /// `compare_exchange` is a [compare-and-swap operation] and thus exhibits the usual downsides + /// of CAS operations. In particular, a load of the value followed by a successful + /// `compare_exchange` with the previous load *does not ensure* that other threads have not + /// changed the value in the interim. This is usually important when the *equality* check in + /// the `compare_exchange` is being used to check the *identity* of a value, but equality + /// does not necessarily imply identity. In this case, `compare_exchange` can lead to the + /// [ABA problem]. + /// + /// [ABA Problem]: https://en.wikipedia.org/wiki/ABA_problem + /// [compare-and-swap operation]: https://en.wikipedia.org/wiki/Compare-and-swap #[inline] #[stable(feature = "extended_compare_and_swap", since = "1.10.0")] #[doc(alias = "compare_and_swap")] @@ -1271,11 +1297,14 @@ impl AtomicBool { /// /// # Considerations /// - /// This method is not magic; it is not provided by the hardware. - /// It is implemented in terms of [`AtomicBool::compare_exchange_weak`], and suffers from the same drawbacks. - /// In particular, this method will not circumvent the [ABA Problem]. + /// This method is not magic; it is not provided by the hardware, and does not act like a + /// critical section or mutex. + /// + /// It is implemented on top of an atomic [compare-and-swap operation], and thus is subject to + /// the usual drawbacks of CAS operations. In particular, be careful of the [ABA problem]. /// /// [ABA Problem]: https://en.wikipedia.org/wiki/ABA_problem + /// [compare-and-swap operation]: https://en.wikipedia.org/wiki/Compare-and-swap /// /// # Examples /// @@ -1338,11 +1367,14 @@ impl AtomicBool { /// /// # Considerations /// - /// This method is not magic; it is not provided by the hardware. - /// It is implemented in terms of [`AtomicBool::compare_exchange_weak`], and suffers from the same drawbacks. - /// In particular, this method will not circumvent the [ABA Problem]. + /// This method is not magic; it is not provided by the hardware, and does not act like a + /// critical section or mutex. + /// + /// It is implemented on top of an atomic [compare-and-swap operation], and thus is subject to + /// the usual drawbacks of CAS operations. In particular, be careful of the [ABA problem]. /// /// [ABA Problem]: https://en.wikipedia.org/wiki/ABA_problem + /// [compare-and-swap operation]: https://en.wikipedia.org/wiki/Compare-and-swap /// /// # Examples /// @@ -1393,11 +1425,14 @@ impl AtomicBool { /// /// # Considerations /// - /// This method is not magic; it is not provided by the hardware. - /// It is implemented in terms of [`AtomicBool::compare_exchange_weak`], and suffers from the same drawbacks. - /// In particular, this method will not circumvent the [ABA Problem]. + /// This method is not magic; it is not provided by the hardware, and does not act like a + /// critical section or mutex. + /// + /// It is implemented on top of an atomic [compare-and-swap operation], and thus is subject to + /// the usual drawbacks of CAS operations. In particular, be careful of the [ABA problem]. /// /// [ABA Problem]: https://en.wikipedia.org/wiki/ABA_problem + /// [compare-and-swap operation]: https://en.wikipedia.org/wiki/Compare-and-swap /// /// # Examples /// @@ -1825,6 +1860,20 @@ impl AtomicPtr { /// let value = some_ptr.compare_exchange(ptr, other_ptr, /// Ordering::SeqCst, Ordering::Relaxed); /// ``` + /// + /// # Considerations + /// + /// `compare_exchange` is a [compare-and-swap operation] and thus exhibits the usual downsides + /// of CAS operations. In particular, a load of the value followed by a successful + /// `compare_exchange` with the previous load *does not ensure* that other threads have not + /// changed the value in the interim. This is usually important when the *equality* check in + /// the `compare_exchange` is being used to check the *identity* of a value, but equality + /// does not necessarily imply identity. This is a particularly common case for pointers, as + /// a pointer holding the same address does not imply that the same object exists at that + /// address! In this case, `compare_exchange` can lead to the [ABA problem]. + /// + /// [ABA Problem]: https://en.wikipedia.org/wiki/ABA_problem + /// [compare-and-swap operation]: https://en.wikipedia.org/wiki/Compare-and-swap #[inline] #[stable(feature = "extended_compare_and_swap", since = "1.10.0")] #[cfg(target_has_atomic = "ptr")] @@ -1874,6 +1923,20 @@ impl AtomicPtr { /// } /// } /// ``` + /// + /// # Considerations + /// + /// `compare_exchange` is a [compare-and-swap operation] and thus exhibits the usual downsides + /// of CAS operations. In particular, a load of the value followed by a successful + /// `compare_exchange` with the previous load *does not ensure* that other threads have not + /// changed the value in the interim. This is usually important when the *equality* check in + /// the `compare_exchange` is being used to check the *identity* of a value, but equality + /// does not necessarily imply identity. This is a particularly common case for pointers, as + /// a pointer holding the same address does not imply that the same object exists at that + /// address! In this case, `compare_exchange` can lead to the [ABA problem]. + /// + /// [ABA Problem]: https://en.wikipedia.org/wiki/ABA_problem + /// [compare-and-swap operation]: https://en.wikipedia.org/wiki/Compare-and-swap #[inline] #[stable(feature = "extended_compare_and_swap", since = "1.10.0")] #[cfg(target_has_atomic = "ptr")] @@ -1917,11 +1980,15 @@ impl AtomicPtr { /// /// # Considerations /// - /// This method is not magic; it is not provided by the hardware. - /// It is implemented in terms of [`AtomicPtr::compare_exchange_weak`], and suffers from the same drawbacks. - /// In particular, this method will not circumvent the [ABA Problem]. + /// This method is not magic; it is not provided by the hardware, and does not act like a + /// critical section or mutex. + /// + /// It is implemented on top of an atomic [compare-and-swap operation], and thus is subject to + /// the usual drawbacks of CAS operations. In particular, be careful of the [ABA problem], + /// which is a particularly common pitfall for pointers! /// /// [ABA Problem]: https://en.wikipedia.org/wiki/ABA_problem + /// [compare-and-swap operation]: https://en.wikipedia.org/wiki/Compare-and-swap /// /// # Examples /// @@ -1992,11 +2059,15 @@ impl AtomicPtr { /// /// # Considerations /// - /// This method is not magic; it is not provided by the hardware. - /// It is implemented in terms of [`AtomicPtr::compare_exchange_weak`], and suffers from the same drawbacks. - /// In particular, this method will not circumvent the [ABA Problem]. + /// This method is not magic; it is not provided by the hardware, and does not act like a + /// critical section or mutex. + /// + /// It is implemented on top of an atomic [compare-and-swap operation], and thus is subject to + /// the usual drawbacks of CAS operations. In particular, be careful of the [ABA problem], + /// which is a particularly common pitfall for pointers! /// /// [ABA Problem]: https://en.wikipedia.org/wiki/ABA_problem + /// [compare-and-swap operation]: https://en.wikipedia.org/wiki/Compare-and-swap /// /// # Examples /// @@ -2057,11 +2128,15 @@ impl AtomicPtr { /// /// # Considerations /// - /// This method is not magic; it is not provided by the hardware. - /// It is implemented in terms of [`AtomicPtr::compare_exchange_weak`], and suffers from the same drawbacks. - /// In particular, this method will not circumvent the [ABA Problem]. + /// This method is not magic; it is not provided by the hardware, and does not act like a + /// critical section or mutex. + /// + /// It is implemented on top of an atomic [compare-and-swap operation], and thus is subject to + /// the usual drawbacks of CAS operations. In particular, be careful of the [ABA problem], + /// which is a particularly common pitfall for pointers! /// /// [ABA Problem]: https://en.wikipedia.org/wiki/ABA_problem + /// [compare-and-swap operation]: https://en.wikipedia.org/wiki/Compare-and-swap /// /// # Examples /// @@ -2967,6 +3042,20 @@ macro_rules! atomic_int { /// Err(10)); /// assert_eq!(some_var.load(Ordering::Relaxed), 10); /// ``` + /// + /// # Considerations + /// + /// `compare_exchange` is a [compare-and-swap operation] and thus exhibits the usual downsides + /// of CAS operations. In particular, a load of the value followed by a successful + /// `compare_exchange` with the previous load *does not ensure* that other threads have not + /// changed the value in the interim! This is usually important when the *equality* check in + /// the `compare_exchange` is being used to check the *identity* of a value, but equality + /// does not necessarily imply identity. This is a particularly common case for pointers, as + /// a pointer holding the same address does not imply that the same object exists at that + /// address! In this case, `compare_exchange` can lead to the [ABA problem]. + /// + /// [ABA Problem]: https://en.wikipedia.org/wiki/ABA_problem + /// [compare-and-swap operation]: https://en.wikipedia.org/wiki/Compare-and-swap #[inline] #[$stable_cxchg] #[$cfg_cas] @@ -3016,6 +3105,20 @@ macro_rules! atomic_int { /// } /// } /// ``` + /// + /// # Considerations + /// + /// `compare_exchange` is a [compare-and-swap operation] and thus exhibits the usual downsides + /// of CAS operations. In particular, a load of the value followed by a successful + /// `compare_exchange` with the previous load *does not ensure* that other threads have not + /// changed the value in the interim. This is usually important when the *equality* check in + /// the `compare_exchange` is being used to check the *identity* of a value, but equality + /// does not necessarily imply identity. This is a particularly common case for pointers, as + /// a pointer holding the same address does not imply that the same object exists at that + /// address! In this case, `compare_exchange` can lead to the [ABA problem]. + /// + /// [ABA Problem]: https://en.wikipedia.org/wiki/ABA_problem + /// [compare-and-swap operation]: https://en.wikipedia.org/wiki/Compare-and-swap #[inline] #[$stable_cxchg] #[$cfg_cas] @@ -3246,13 +3349,16 @@ macro_rules! atomic_int { /// /// # Considerations /// - /// This method is not magic; it is not provided by the hardware. - /// It is implemented in terms of - #[doc = concat!("[`", stringify!($atomic_type), "::compare_exchange_weak`],")] - /// and suffers from the same drawbacks. - /// In particular, this method will not circumvent the [ABA Problem]. + /// This method is not magic; it is not provided by the hardware, and does not act like a + /// critical section or mutex. + /// + /// It is implemented on top of an atomic [compare-and-swap operation], and thus is subject to + /// the usual drawbacks of CAS operations. In particular, be careful of the [ABA problem] + /// if this atomic integer is an index or more generally if knowledge of only the *bitwise value* + /// of the atomic is not in and of itself sufficient to ensure any required preconditions. /// /// [ABA Problem]: https://en.wikipedia.org/wiki/ABA_problem + /// [compare-and-swap operation]: https://en.wikipedia.org/wiki/Compare-and-swap /// /// # Examples /// @@ -3309,13 +3415,16 @@ macro_rules! atomic_int { /// /// # Considerations /// - /// This method is not magic; it is not provided by the hardware. - /// It is implemented in terms of - #[doc = concat!("[`", stringify!($atomic_type), "::compare_exchange_weak`],")] - /// and suffers from the same drawbacks. - /// In particular, this method will not circumvent the [ABA Problem]. + /// This method is not magic; it is not provided by the hardware, and does not act like a + /// critical section or mutex. + /// + /// It is implemented on top of an atomic [compare-and-swap operation], and thus is subject to + /// the usual drawbacks of CAS operations. In particular, be careful of the [ABA problem] + /// if this atomic integer is an index or more generally if knowledge of only the *bitwise value* + /// of the atomic is not in and of itself sufficient to ensure any required preconditions. /// /// [ABA Problem]: https://en.wikipedia.org/wiki/ABA_problem + /// [compare-and-swap operation]: https://en.wikipedia.org/wiki/Compare-and-swap /// /// # Examples /// @@ -3367,13 +3476,17 @@ macro_rules! atomic_int { /// /// # Considerations /// - /// This method is not magic; it is not provided by the hardware. - /// It is implemented in terms of - #[doc = concat!("[`", stringify!($atomic_type), "::compare_exchange_weak`],")] - /// and suffers from the same drawbacks. - /// In particular, this method will not circumvent the [ABA Problem]. + /// [CAS operation]: https://en.wikipedia.org/wiki/Compare-and-swap + /// This method is not magic; it is not provided by the hardware, and does not act like a + /// critical section or mutex. + /// + /// It is implemented on top of an atomic [compare-and-swap operation], and thus is subject to + /// the usual drawbacks of CAS operations. In particular, be careful of the [ABA problem] + /// if this atomic integer is an index or more generally if knowledge of only the *bitwise value* + /// of the atomic is not in and of itself sufficient to ensure any required preconditions. /// /// [ABA Problem]: https://en.wikipedia.org/wiki/ABA_problem + /// [compare-and-swap operation]: https://en.wikipedia.org/wiki/Compare-and-swap /// /// # Examples /// From 2a10f1211274db3078f047278fac9648edd32d5e Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 11 Jun 2025 08:40:46 +0200 Subject: [PATCH 16/18] miri: add flag to suppress float non-determinism --- src/tools/miri/README.md | 5 +++++ src/tools/miri/src/bin/miri.rs | 2 ++ src/tools/miri/src/eval.rs | 3 +++ src/tools/miri/src/intrinsics/mod.rs | 4 ++-- src/tools/miri/src/intrinsics/simd.rs | 3 ++- src/tools/miri/src/machine.rs | 5 +++++ src/tools/miri/src/math.rs | 4 ++++ src/tools/miri/src/operator.rs | 9 ++++++++- 8 files changed, 31 insertions(+), 4 deletions(-) diff --git a/src/tools/miri/README.md b/src/tools/miri/README.md index fc7942a49c0cc..5554c7975ff3b 100644 --- a/src/tools/miri/README.md +++ b/src/tools/miri/README.md @@ -286,6 +286,11 @@ environment variable. We first document the most relevant and most commonly used specific circumstances, but Miri's behavior will also be more stable across versions and targets. This is equivalent to `-Zmiri-fixed-schedule -Zmiri-compare-exchange-weak-failure-rate=0.0 -Zmiri-address-reuse-cross-thread-rate=0.0 -Zmiri-disable-weak-memory-emulation`. +* `-Zmiri-deterministic-floats` makes Miri's floating-point behavior fully deterministic. This means + that operations will always return the preferred NaN, imprecise operations will not have any + random error applied to them, and `min`/`max` as "maybe fused" multiply-add all behave + deterministically. Note that Miri still uses host floats for some operations, so behavior can + still differ depending on the host target and setup. * `-Zmiri-disable-isolation` disables host isolation. As a consequence, the program has access to host resources such as environment variables, file systems, and randomness. diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs index 2faaec5a1744d..d4ba7fbd6a479 100644 --- a/src/tools/miri/src/bin/miri.rs +++ b/src/tools/miri/src/bin/miri.rs @@ -601,6 +601,8 @@ fn main() { miri_config.collect_leak_backtraces = false; } else if arg == "-Zmiri-force-intrinsic-fallback" { miri_config.force_intrinsic_fallback = true; + } else if arg == "-Zmiri-deterministic-floats" { + miri_config.float_nondet = false; } else if arg == "-Zmiri-strict-provenance" { miri_config.provenance_mode = ProvenanceMode::Strict; } else if arg == "-Zmiri-permissive-provenance" { diff --git a/src/tools/miri/src/eval.rs b/src/tools/miri/src/eval.rs index 6f5f756e14416..7a5f96ec1779d 100644 --- a/src/tools/miri/src/eval.rs +++ b/src/tools/miri/src/eval.rs @@ -166,6 +166,8 @@ pub struct MiriConfig { pub fixed_scheduling: bool, /// Always prefer the intrinsic fallback body over the native Miri implementation. pub force_intrinsic_fallback: bool, + /// Whether floating-point operations can behave non-deterministically. + pub float_nondet: bool, } impl Default for MiriConfig { @@ -205,6 +207,7 @@ impl Default for MiriConfig { address_reuse_cross_thread_rate: 0.1, fixed_scheduling: false, force_intrinsic_fallback: false, + float_nondet: true, } } } diff --git a/src/tools/miri/src/intrinsics/mod.rs b/src/tools/miri/src/intrinsics/mod.rs index 9957e351ff105..c5f73428b57ee 100644 --- a/src/tools/miri/src/intrinsics/mod.rs +++ b/src/tools/miri/src/intrinsics/mod.rs @@ -293,7 +293,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let a = this.read_scalar(a)?.to_f32()?; let b = this.read_scalar(b)?.to_f32()?; let c = this.read_scalar(c)?.to_f32()?; - let fuse: bool = this.machine.rng.get_mut().random(); + let fuse: bool = this.machine.float_nondet && this.machine.rng.get_mut().random(); let res = if fuse { // FIXME: Using host floats, to work around https://github.com/rust-lang/rustc_apfloat/issues/11 a.to_host().mul_add(b.to_host(), c.to_host()).to_soft() @@ -308,7 +308,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let a = this.read_scalar(a)?.to_f64()?; let b = this.read_scalar(b)?.to_f64()?; let c = this.read_scalar(c)?.to_f64()?; - let fuse: bool = this.machine.rng.get_mut().random(); + let fuse: bool = this.machine.float_nondet && this.machine.rng.get_mut().random(); let res = if fuse { // FIXME: Using host floats, to work around https://github.com/rust-lang/rustc_apfloat/issues/11 a.to_host().mul_add(b.to_host(), c.to_host()).to_soft() diff --git a/src/tools/miri/src/intrinsics/simd.rs b/src/tools/miri/src/intrinsics/simd.rs index b17fd4fb7f932..9f2041731b2ec 100644 --- a/src/tools/miri/src/intrinsics/simd.rs +++ b/src/tools/miri/src/intrinsics/simd.rs @@ -306,7 +306,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let c = this.read_scalar(&this.project_index(&c, i)?)?; let dest = this.project_index(&dest, i)?; - let fuse: bool = intrinsic_name == "fma" || this.machine.rng.get_mut().random(); + let fuse: bool = intrinsic_name == "fma" + || (this.machine.float_nondet && this.machine.rng.get_mut().random()); // Works for f32 and f64. // FIXME: using host floats to work around https://github.com/rust-lang/miri/issues/2468. diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index b221dd8509251..b4d7db34efa7b 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -618,6 +618,9 @@ pub struct MiriMachine<'tcx> { /// Always prefer the intrinsic fallback body over the native Miri implementation. pub force_intrinsic_fallback: bool, + + /// Whether floating-point operations can behave non-deterministically. + pub float_nondet: bool, } impl<'tcx> MiriMachine<'tcx> { @@ -778,6 +781,7 @@ impl<'tcx> MiriMachine<'tcx> { int2ptr_warned: Default::default(), mangle_internal_symbol_cache: Default::default(), force_intrinsic_fallback: config.force_intrinsic_fallback, + float_nondet: config.float_nondet, } } @@ -956,6 +960,7 @@ impl VisitProvenance for MiriMachine<'_> { int2ptr_warned: _, mangle_internal_symbol_cache: _, force_intrinsic_fallback: _, + float_nondet: _, } = self; threads.visit_provenance(visit); diff --git a/src/tools/miri/src/math.rs b/src/tools/miri/src/math.rs index d1355a2168470..cf16a5676d680 100644 --- a/src/tools/miri/src/math.rs +++ b/src/tools/miri/src/math.rs @@ -15,6 +15,10 @@ pub(crate) fn apply_random_float_error( val: F, err_scale: i32, ) -> F { + if !ecx.machine.float_nondet { + return val; + } + let rng = ecx.machine.rng.get_mut(); // Generate a random integer in the range [0, 2^PREC). // (When read as binary, the position of the first `1` determines the exponent, diff --git a/src/tools/miri/src/operator.rs b/src/tools/miri/src/operator.rs index 81f22b2d0b2a3..73d671121f653 100644 --- a/src/tools/miri/src/operator.rs +++ b/src/tools/miri/src/operator.rs @@ -76,6 +76,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } fn generate_nan, F2: Float>(&self, inputs: &[F1]) -> F2 { + let this = self.eval_context_ref(); + if !this.machine.float_nondet { + return F2::NAN; + } + /// Make the given NaN a signaling NaN. /// Returns `None` if this would not result in a NaN. fn make_signaling(f: F) -> Option { @@ -89,7 +94,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if f.is_nan() { Some(f) } else { None } } - let this = self.eval_context_ref(); let mut rand = this.machine.rng.borrow_mut(); // Assemble an iterator of possible NaNs: preferred, quieting propagation, unchanged propagation. // On some targets there are more possibilities; for now we just generate those options that @@ -118,6 +122,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn equal_float_min_max(&self, a: F, b: F) -> F { let this = self.eval_context_ref(); + if !this.machine.float_nondet { + return a; + } // Return one side non-deterministically. let mut rand = this.machine.rng.borrow_mut(); if rand.random() { a } else { b } From 86493f75a5bf0b4ed45f6dc082a81f692dc4cb23 Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Wed, 11 Jun 2025 01:56:47 -0700 Subject: [PATCH 17/18] compiler: Avoid reporting unsupported_calling_conventions in deps --- compiler/rustc_lint_defs/src/builtin.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 777118e69fb15..2b15564dcfaa4 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -3664,7 +3664,7 @@ declare_lint! { "use of unsupported calling convention", @future_incompatible = FutureIncompatibleInfo { reason: FutureIncompatibilityReason::FutureReleaseError, - report_in_deps: true, + report_in_deps: false, reference: "issue #137018 ", }; } From 8f0c09aa502359ee041309fac434409af86d3366 Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Wed, 11 Jun 2025 09:58:52 -0700 Subject: [PATCH 18/18] tests: Bless for weaker unsupported_calling_conventions --- tests/ui/abi/unsupported.aarch64.stderr | 48 ----------- tests/ui/abi/unsupported.arm.stderr | 48 ----------- tests/ui/abi/unsupported.riscv32.stderr | 48 ----------- tests/ui/abi/unsupported.riscv64.stderr | 48 ----------- tests/ui/abi/unsupported.x64.stderr | 48 ----------- tests/ui/abi/unsupported.x64_win.stderr | 108 ------------------------ 6 files changed, 348 deletions(-) diff --git a/tests/ui/abi/unsupported.aarch64.stderr b/tests/ui/abi/unsupported.aarch64.stderr index 22dca00e3b293..4721c26026d18 100644 --- a/tests/ui/abi/unsupported.aarch64.stderr +++ b/tests/ui/abi/unsupported.aarch64.stderr @@ -368,42 +368,6 @@ LL | fn stdcall_ptr(f: extern "stdcall" fn()) { = note: for more information, see issue #130260 = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default -Future breakage diagnostic: -warning: use of calling convention not supported on this target - --> $DIR/unsupported.rs:131:17 - | -LL | fn cdecl_ptr(f: extern "cdecl" fn()) { - | ^^^^^^^^^^^^^^^^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #137018 - = help: use `extern "C"` instead - = note: `#[warn(unsupported_calling_conventions)]` on by default - -Future breakage diagnostic: -warning: use of calling convention not supported on this target - --> $DIR/unsupported.rs:136:1 - | -LL | extern "cdecl" {} - | ^^^^^^^^^^^^^^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #137018 - = help: use `extern "C"` instead - = note: `#[warn(unsupported_calling_conventions)]` on by default - -Future breakage diagnostic: -warning: use of calling convention not supported on this target - --> $DIR/unsupported.rs:139:1 - | -LL | extern "cdecl-unwind" {} - | ^^^^^^^^^^^^^^^^^^^^^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #137018 - = help: use `extern "C-unwind"` instead - = note: `#[warn(unsupported_calling_conventions)]` on by default - Future breakage diagnostic: warning: the calling convention "vectorcall" is not supported on this target --> $DIR/unsupported.rs:145:22 @@ -437,15 +401,3 @@ LL | fn cmse_entry_ptr(f: extern "C-cmse-nonsecure-entry" fn()) { = note: for more information, see issue #130260 = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default -Future breakage diagnostic: -warning: use of calling convention not supported on this target - --> $DIR/unsupported.rs:128:1 - | -LL | extern "cdecl" fn cdecl() {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #137018 - = help: use `extern "C"` instead - = note: `#[warn(unsupported_calling_conventions)]` on by default - diff --git a/tests/ui/abi/unsupported.arm.stderr b/tests/ui/abi/unsupported.arm.stderr index 0ac6d888f8dc6..ed9cd2ab2c5dc 100644 --- a/tests/ui/abi/unsupported.arm.stderr +++ b/tests/ui/abi/unsupported.arm.stderr @@ -336,42 +336,6 @@ LL | fn stdcall_ptr(f: extern "stdcall" fn()) { = note: for more information, see issue #130260 = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default -Future breakage diagnostic: -warning: use of calling convention not supported on this target - --> $DIR/unsupported.rs:131:17 - | -LL | fn cdecl_ptr(f: extern "cdecl" fn()) { - | ^^^^^^^^^^^^^^^^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #137018 - = help: use `extern "C"` instead - = note: `#[warn(unsupported_calling_conventions)]` on by default - -Future breakage diagnostic: -warning: use of calling convention not supported on this target - --> $DIR/unsupported.rs:136:1 - | -LL | extern "cdecl" {} - | ^^^^^^^^^^^^^^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #137018 - = help: use `extern "C"` instead - = note: `#[warn(unsupported_calling_conventions)]` on by default - -Future breakage diagnostic: -warning: use of calling convention not supported on this target - --> $DIR/unsupported.rs:139:1 - | -LL | extern "cdecl-unwind" {} - | ^^^^^^^^^^^^^^^^^^^^^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #137018 - = help: use `extern "C-unwind"` instead - = note: `#[warn(unsupported_calling_conventions)]` on by default - Future breakage diagnostic: warning: the calling convention "vectorcall" is not supported on this target --> $DIR/unsupported.rs:145:22 @@ -405,15 +369,3 @@ LL | fn cmse_entry_ptr(f: extern "C-cmse-nonsecure-entry" fn()) { = note: for more information, see issue #130260 = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default -Future breakage diagnostic: -warning: use of calling convention not supported on this target - --> $DIR/unsupported.rs:128:1 - | -LL | extern "cdecl" fn cdecl() {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #137018 - = help: use `extern "C"` instead - = note: `#[warn(unsupported_calling_conventions)]` on by default - diff --git a/tests/ui/abi/unsupported.riscv32.stderr b/tests/ui/abi/unsupported.riscv32.stderr index ad57a89e45531..9e75dfafca0f5 100644 --- a/tests/ui/abi/unsupported.riscv32.stderr +++ b/tests/ui/abi/unsupported.riscv32.stderr @@ -336,42 +336,6 @@ LL | fn stdcall_ptr(f: extern "stdcall" fn()) { = note: for more information, see issue #130260 = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default -Future breakage diagnostic: -warning: use of calling convention not supported on this target - --> $DIR/unsupported.rs:131:17 - | -LL | fn cdecl_ptr(f: extern "cdecl" fn()) { - | ^^^^^^^^^^^^^^^^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #137018 - = help: use `extern "C"` instead - = note: `#[warn(unsupported_calling_conventions)]` on by default - -Future breakage diagnostic: -warning: use of calling convention not supported on this target - --> $DIR/unsupported.rs:136:1 - | -LL | extern "cdecl" {} - | ^^^^^^^^^^^^^^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #137018 - = help: use `extern "C"` instead - = note: `#[warn(unsupported_calling_conventions)]` on by default - -Future breakage diagnostic: -warning: use of calling convention not supported on this target - --> $DIR/unsupported.rs:139:1 - | -LL | extern "cdecl-unwind" {} - | ^^^^^^^^^^^^^^^^^^^^^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #137018 - = help: use `extern "C-unwind"` instead - = note: `#[warn(unsupported_calling_conventions)]` on by default - Future breakage diagnostic: warning: the calling convention "vectorcall" is not supported on this target --> $DIR/unsupported.rs:145:22 @@ -405,15 +369,3 @@ LL | fn cmse_entry_ptr(f: extern "C-cmse-nonsecure-entry" fn()) { = note: for more information, see issue #130260 = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default -Future breakage diagnostic: -warning: use of calling convention not supported on this target - --> $DIR/unsupported.rs:128:1 - | -LL | extern "cdecl" fn cdecl() {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #137018 - = help: use `extern "C"` instead - = note: `#[warn(unsupported_calling_conventions)]` on by default - diff --git a/tests/ui/abi/unsupported.riscv64.stderr b/tests/ui/abi/unsupported.riscv64.stderr index ad57a89e45531..9e75dfafca0f5 100644 --- a/tests/ui/abi/unsupported.riscv64.stderr +++ b/tests/ui/abi/unsupported.riscv64.stderr @@ -336,42 +336,6 @@ LL | fn stdcall_ptr(f: extern "stdcall" fn()) { = note: for more information, see issue #130260 = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default -Future breakage diagnostic: -warning: use of calling convention not supported on this target - --> $DIR/unsupported.rs:131:17 - | -LL | fn cdecl_ptr(f: extern "cdecl" fn()) { - | ^^^^^^^^^^^^^^^^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #137018 - = help: use `extern "C"` instead - = note: `#[warn(unsupported_calling_conventions)]` on by default - -Future breakage diagnostic: -warning: use of calling convention not supported on this target - --> $DIR/unsupported.rs:136:1 - | -LL | extern "cdecl" {} - | ^^^^^^^^^^^^^^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #137018 - = help: use `extern "C"` instead - = note: `#[warn(unsupported_calling_conventions)]` on by default - -Future breakage diagnostic: -warning: use of calling convention not supported on this target - --> $DIR/unsupported.rs:139:1 - | -LL | extern "cdecl-unwind" {} - | ^^^^^^^^^^^^^^^^^^^^^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #137018 - = help: use `extern "C-unwind"` instead - = note: `#[warn(unsupported_calling_conventions)]` on by default - Future breakage diagnostic: warning: the calling convention "vectorcall" is not supported on this target --> $DIR/unsupported.rs:145:22 @@ -405,15 +369,3 @@ LL | fn cmse_entry_ptr(f: extern "C-cmse-nonsecure-entry" fn()) { = note: for more information, see issue #130260 = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default -Future breakage diagnostic: -warning: use of calling convention not supported on this target - --> $DIR/unsupported.rs:128:1 - | -LL | extern "cdecl" fn cdecl() {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #137018 - = help: use `extern "C"` instead - = note: `#[warn(unsupported_calling_conventions)]` on by default - diff --git a/tests/ui/abi/unsupported.x64.stderr b/tests/ui/abi/unsupported.x64.stderr index f777fe86e2417..5b55e5707fad9 100644 --- a/tests/ui/abi/unsupported.x64.stderr +++ b/tests/ui/abi/unsupported.x64.stderr @@ -315,42 +315,6 @@ LL | fn stdcall_ptr(f: extern "stdcall" fn()) { = note: for more information, see issue #130260 = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default -Future breakage diagnostic: -warning: use of calling convention not supported on this target - --> $DIR/unsupported.rs:131:17 - | -LL | fn cdecl_ptr(f: extern "cdecl" fn()) { - | ^^^^^^^^^^^^^^^^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #137018 - = help: use `extern "C"` instead - = note: `#[warn(unsupported_calling_conventions)]` on by default - -Future breakage diagnostic: -warning: use of calling convention not supported on this target - --> $DIR/unsupported.rs:136:1 - | -LL | extern "cdecl" {} - | ^^^^^^^^^^^^^^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #137018 - = help: use `extern "C"` instead - = note: `#[warn(unsupported_calling_conventions)]` on by default - -Future breakage diagnostic: -warning: use of calling convention not supported on this target - --> $DIR/unsupported.rs:139:1 - | -LL | extern "cdecl-unwind" {} - | ^^^^^^^^^^^^^^^^^^^^^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #137018 - = help: use `extern "C-unwind"` instead - = note: `#[warn(unsupported_calling_conventions)]` on by default - Future breakage diagnostic: warning: the calling convention "C-cmse-nonsecure-call" is not supported on this target --> $DIR/unsupported.rs:153:21 @@ -373,15 +337,3 @@ LL | fn cmse_entry_ptr(f: extern "C-cmse-nonsecure-entry" fn()) { = note: for more information, see issue #130260 = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default -Future breakage diagnostic: -warning: use of calling convention not supported on this target - --> $DIR/unsupported.rs:128:1 - | -LL | extern "cdecl" fn cdecl() {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #137018 - = help: use `extern "C"` instead - = note: `#[warn(unsupported_calling_conventions)]` on by default - diff --git a/tests/ui/abi/unsupported.x64_win.stderr b/tests/ui/abi/unsupported.x64_win.stderr index 328f4c3b04338..93b5a272e926d 100644 --- a/tests/ui/abi/unsupported.x64_win.stderr +++ b/tests/ui/abi/unsupported.x64_win.stderr @@ -321,78 +321,6 @@ LL | fn thiscall_ptr(f: extern "thiscall" fn()) { = note: for more information, see issue #130260 = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default -Future breakage diagnostic: -warning: use of calling convention not supported on this target - --> $DIR/unsupported.rs:112:19 - | -LL | fn stdcall_ptr(f: extern "stdcall" fn()) { - | ^^^^^^^^^^^^^^^^^^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #137018 - = help: if you need `extern "stdcall"` on win32 and `extern "C"` everywhere else, use `extern "system"` - = note: `#[warn(unsupported_calling_conventions)]` on by default - -Future breakage diagnostic: -warning: use of calling convention not supported on this target - --> $DIR/unsupported.rs:119:1 - | -LL | extern "stdcall" {} - | ^^^^^^^^^^^^^^^^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #137018 - = help: if you need `extern "stdcall"` on win32 and `extern "C"` everywhere else, use `extern "system"` - = note: `#[warn(unsupported_calling_conventions)]` on by default - -Future breakage diagnostic: -warning: use of calling convention not supported on this target - --> $DIR/unsupported.rs:123:1 - | -LL | extern "stdcall-unwind" {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #137018 - = help: if you need `extern "stdcall-unwind"` on win32 and `extern "C-unwind"` everywhere else, use `extern "system-unwind"` - = note: `#[warn(unsupported_calling_conventions)]` on by default - -Future breakage diagnostic: -warning: use of calling convention not supported on this target - --> $DIR/unsupported.rs:131:17 - | -LL | fn cdecl_ptr(f: extern "cdecl" fn()) { - | ^^^^^^^^^^^^^^^^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #137018 - = help: use `extern "C"` instead - = note: `#[warn(unsupported_calling_conventions)]` on by default - -Future breakage diagnostic: -warning: use of calling convention not supported on this target - --> $DIR/unsupported.rs:136:1 - | -LL | extern "cdecl" {} - | ^^^^^^^^^^^^^^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #137018 - = help: use `extern "C"` instead - = note: `#[warn(unsupported_calling_conventions)]` on by default - -Future breakage diagnostic: -warning: use of calling convention not supported on this target - --> $DIR/unsupported.rs:139:1 - | -LL | extern "cdecl-unwind" {} - | ^^^^^^^^^^^^^^^^^^^^^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #137018 - = help: use `extern "C-unwind"` instead - = note: `#[warn(unsupported_calling_conventions)]` on by default - Future breakage diagnostic: warning: the calling convention "C-cmse-nonsecure-call" is not supported on this target --> $DIR/unsupported.rs:153:21 @@ -415,39 +343,3 @@ LL | fn cmse_entry_ptr(f: extern "C-cmse-nonsecure-entry" fn()) { = note: for more information, see issue #130260 = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default -Future breakage diagnostic: -warning: use of calling convention not supported on this target - --> $DIR/unsupported.rs:171:1 - | -LL | extern "cdecl" {} - | ^^^^^^^^^^^^^^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #137018 - = help: use `extern "C"` instead - = note: `#[warn(unsupported_calling_conventions)]` on by default - -Future breakage diagnostic: -warning: use of calling convention not supported on this target - --> $DIR/unsupported.rs:108:1 - | -LL | extern "stdcall" fn stdcall() {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #137018 - = help: if you need `extern "stdcall"` on win32 and `extern "C"` everywhere else, use `extern "system"` - = note: `#[warn(unsupported_calling_conventions)]` on by default - -Future breakage diagnostic: -warning: use of calling convention not supported on this target - --> $DIR/unsupported.rs:128:1 - | -LL | extern "cdecl" fn cdecl() {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #137018 - = help: use `extern "C"` instead - = note: `#[warn(unsupported_calling_conventions)]` on by default -