Skip to content

Commit

Permalink
Stabilize the #[diagnostic] namespace and `#[diagnostic::on_unimple…
Browse files Browse the repository at this point in the history
…mented]` attribute

This PR stabilizes the `#[diagnostic]` attribute namespace and a minimal
option of the `#[diagnostic::on_unimplemented]` attribute.

The `#[diagnostic]` attribute namespace is meant to provide a home for
attribute that allow users to influence error messages emitted by the
compiler. The compiler is not guaranteed to use any of this hints,
however it should accept any (non-)existing attribute in this namespace
and potentially emit lint-warnings for unused attributes and options.
This is meant to allow discarding certain attributes/options in the
future to allow fundamental changes to the compiler without the need to
keep then non-meaningful options working.

The `#[diagnostic::on_unimplemented]` attribute is allowed to appear
on a trait definition. This allows crate authors to hint the compiler
to emit a specific error message if a certain trait is not implemented.
For the `#[diagnostic::on_unimplemented]` attribute the following
options are implemented:

* `message` which provides the text for the top level error message
* `label` which provides the text for the label shown inline in the
broken code in the error message
* `note` which provides additional notes.

The `note` option can appear several times, which results in several
note messages being emitted. If any of the other options appears several
times the first occurrence of the relevant option specifies the actually
used value. Any other occurrence generates an lint warning. For any
other non-existing option a lint-warning is generated.

All three options accept a text as argument. This text is allowed to
contain format parameters referring to generic argument or `Self` by
name via the `{Self}` or `{NameOfGenericArgument}` syntax. For any
non-existing argument a lint warning is generated.

Tracking issue: #111996
  • Loading branch information
weiznich committed Jan 12, 2024
1 parent 2b1365b commit 4353333
Show file tree
Hide file tree
Showing 33 changed files with 262 additions and 353 deletions.
8 changes: 0 additions & 8 deletions compiler/rustc_ast_passes/src/feature_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -209,14 +209,6 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
);
}
}
if !attr.is_doc_comment()
&& let [seg, _] = attr.get_normal_item().path.segments.as_slice()
&& seg.ident.name == sym::diagnostic
&& !self.features.diagnostic_namespace
{
let msg = "`#[diagnostic]` attribute name space is experimental";
gate!(self, diagnostic_namespace, seg.ident.span, msg);
}

// Emit errors for non-staged-api crates.
if !self.features.staged_api {
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_feature/src/accepted.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,8 @@ declare_features! (
(accepted, derive_default_enum, "1.62.0", Some(86985)),
/// Allows the use of destructuring assignments.
(accepted, destructuring_assignment, "1.59.0", Some(71126)),
/// Allows using the `#[diagnostic]` attribute tool namespace
(accepted, diagnostic_namespace, "CURRENT_RUSTC_VERSION", Some(111996)),
/// Allows `#[doc(alias = "...")]`.
(accepted, doc_alias, "1.48.0", Some(50146)),
/// Allows `..` in tuple (struct) patterns.
Expand Down
2 changes: 0 additions & 2 deletions compiler/rustc_feature/src/unstable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -436,8 +436,6 @@ declare_features! (
(unstable, deprecated_safe, "1.61.0", Some(94978)),
/// Allows having using `suggestion` in the `#[deprecated]` attribute.
(unstable, deprecated_suggestion, "1.61.0", Some(94785)),
/// Allows using the `#[diagnostic]` attribute tool namespace
(unstable, diagnostic_namespace, "1.73.0", Some(111996)),
/// Controls errors in trait implementations.
(unstable, do_not_recommend, "1.67.0", Some(51992)),
/// Tells rustdoc to automatically generate `#[doc(cfg(...))]`.
Expand Down
11 changes: 7 additions & 4 deletions library/core/src/future/future.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,13 @@ use crate::task::{Context, Poll};
#[must_use = "futures do nothing unless you `.await` or poll them"]
#[stable(feature = "futures_api", since = "1.36.0")]
#[lang = "future_trait"]
#[diagnostic::on_unimplemented(
label = "`{Self}` is not a future",
message = "`{Self}` is not a future",
note = "{Self} must be a future or must implement `IntoFuture` to be awaited"
#[cfg_attr(
not(bootstrap),
diagnostic::on_unimplemented(
label = "`{Self}` is not a future",
message = "`{Self}` is not a future",
note = "{Self} must be a future or must implement `IntoFuture` to be awaited"
)
)]
pub trait Future {
/// The type of value produced on completion.
Expand Down
1 change: 0 additions & 1 deletion library/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,6 @@
#![feature(const_trait_impl)]
#![feature(decl_macro)]
#![feature(deprecated_suggestion)]
#![feature(diagnostic_namespace)]
#![feature(doc_cfg)]
#![feature(doc_cfg_hide)]
#![feature(doc_notable_trait)]
Expand Down
53 changes: 37 additions & 16 deletions library/core/src/marker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,12 @@ macro marker_impls {
/// [ub]: ../../reference/behavior-considered-undefined.html
#[stable(feature = "rust1", since = "1.0.0")]
#[cfg_attr(not(test), rustc_diagnostic_item = "Send")]
#[diagnostic::on_unimplemented(
message = "`{Self}` cannot be sent between threads safely",
label = "`{Self}` cannot be sent between threads safely"
#[cfg_attr(
not(bootstrap),
diagnostic::on_unimplemented(
message = "`{Self}` cannot be sent between threads safely",
label = "`{Self}` cannot be sent between threads safely"
)
)]
pub unsafe auto trait Send {
// empty.
Expand Down Expand Up @@ -134,9 +137,12 @@ unsafe impl<T: Sync + ?Sized> Send for &T {}
#[doc(alias = "?", alias = "?Sized")]
#[stable(feature = "rust1", since = "1.0.0")]
#[lang = "sized"]
#[diagnostic::on_unimplemented(
message = "the size for values of type `{Self}` cannot be known at compilation time",
label = "doesn't have a size known at compile-time"
#[cfg_attr(
not(bootstrap),
diagnostic::on_unimplemented(
message = "the size for values of type `{Self}` cannot be known at compilation time",
label = "doesn't have a size known at compile-time"
)
)]
#[fundamental] // for Default, for example, which requires that `[T]: !Default` be evaluatable
#[rustc_specialization_trait]
Expand Down Expand Up @@ -205,7 +211,10 @@ pub trait Unsize<T: ?Sized> {
/// [RFC1445]: https://github.com/rust-lang/rfcs/blob/master/text/1445-restrict-constants-in-patterns.md
/// [issue 63438]: https://github.com/rust-lang/rust/issues/63438
#[unstable(feature = "structural_match", issue = "31434")]
#[diagnostic::on_unimplemented(message = "the type `{Self}` does not `#[derive(PartialEq)]`")]
#[cfg_attr(
not(bootstrap),
diagnostic::on_unimplemented(message = "the type `{Self}` does not `#[derive(PartialEq)]`")
)]
#[lang = "structural_peq"]
pub trait StructuralPartialEq {
// Empty.
Expand Down Expand Up @@ -273,7 +282,10 @@ marker_impls! {
/// of the two derives (`#[derive(PartialEq)]` and `#[derive(Eq)]`) and check
/// that both of them are present as part of structural-match checking.
#[unstable(feature = "structural_match", issue = "31434")]
#[diagnostic::on_unimplemented(message = "the type `{Self}` does not `#[derive(Eq)]`")]
#[cfg_attr(
not(bootstrap),
diagnostic::on_unimplemented(message = "the type `{Self}` does not `#[derive(Eq)]`")
)]
#[lang = "structural_teq"]
pub trait StructuralEq {
// Empty.
Expand Down Expand Up @@ -964,9 +976,12 @@ marker_impls! {
/// [section about `Unpin`]: crate::pin#unpin "pin module docs about unpin"
/// [`unsafe`]: ../../std/keyword.unsafe.html "keyword unsafe"
#[stable(feature = "pin", since = "1.33.0")]
#[diagnostic::on_unimplemented(
note = "consider using the `pin!` macro\nconsider using `Box::pin` if you need to access the pinned value outside of the current scope",
message = "`{Self}` cannot be unpinned"
#[cfg_attr(
not(bootstrap),
diagnostic::on_unimplemented(
note = "consider using the `pin!` macro\nconsider using `Box::pin` if you need to access the pinned value outside of the current scope",
message = "`{Self}` cannot be unpinned"
)
)]
#[lang = "unpin"]
pub auto trait Unpin {}
Expand Down Expand Up @@ -1012,7 +1027,7 @@ pub trait Destruct {}
/// for any user type.
#[unstable(feature = "tuple_trait", issue = "none")]
#[lang = "tuple_trait"]
#[diagnostic::on_unimplemented(message = "`{Self}` is not a tuple")]
#[cfg_attr(not(bootstrap), diagnostic::on_unimplemented(message = "`{Self}` is not a tuple"))]
#[rustc_deny_explicit_impl(implement_via_object = false)]
pub trait Tuple {}

Expand All @@ -1022,9 +1037,12 @@ pub trait Tuple {}
/// `*const ()` automatically implement this trait.
#[unstable(feature = "pointer_like_trait", issue = "none")]
#[lang = "pointer_like"]
#[diagnostic::on_unimplemented(
message = "`{Self}` needs to have the same ABI as a pointer",
label = "`{Self}` needs to be a pointer-like type"
#[cfg_attr(
not(bootstrap),
diagnostic::on_unimplemented(
message = "`{Self}` needs to have the same ABI as a pointer",
label = "`{Self}` needs to be a pointer-like type"
)
)]
pub trait PointerLike {}

Expand All @@ -1036,7 +1054,10 @@ pub trait PointerLike {}
/// are `StructuralPartialEq`.
#[lang = "const_param_ty"]
#[unstable(feature = "adt_const_params", issue = "95174")]
#[diagnostic::on_unimplemented(message = "`{Self}` can't be used as a const parameter type")]
#[cfg_attr(
not(bootstrap),
diagnostic::on_unimplemented(message = "`{Self}` can't be used as a const parameter type")
)]
#[allow(multiple_supertrait_upcastable)]
pub trait ConstParamTy: StructuralEq + StructuralPartialEq + Eq {}

Expand Down
72 changes: 48 additions & 24 deletions library/core/src/ops/arith.rs
Original file line number Diff line number Diff line change
Expand Up @@ -307,9 +307,12 @@ sub_impl! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f32 f64 }
/// ```
#[lang = "mul"]
#[stable(feature = "rust1", since = "1.0.0")]
#[diagnostic::on_unimplemented(
message = "cannot multiply `{Self}` by `{Rhs}`",
label = "no implementation for `{Self} * {Rhs}`"
#[cfg_attr(
not(bootstrap),
diagnostic::on_unimplemented(
message = "cannot multiply `{Self}` by `{Rhs}`",
label = "no implementation for `{Self} * {Rhs}`"
)
)]
#[doc(alias = "*")]
pub trait Mul<Rhs = Self> {
Expand Down Expand Up @@ -441,9 +444,12 @@ mul_impl! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f32 f64 }
/// ```
#[lang = "div"]
#[stable(feature = "rust1", since = "1.0.0")]
#[diagnostic::on_unimplemented(
message = "cannot divide `{Self}` by `{Rhs}`",
label = "no implementation for `{Self} / {Rhs}`"
#[cfg_attr(
not(bootstrap),
diagnostic::on_unimplemented(
message = "cannot divide `{Self}` by `{Rhs}`",
label = "no implementation for `{Self} / {Rhs}`"
)
)]
#[doc(alias = "/")]
pub trait Div<Rhs = Self> {
Expand Down Expand Up @@ -543,9 +549,12 @@ div_impl_float! { f32 f64 }
/// ```
#[lang = "rem"]
#[stable(feature = "rust1", since = "1.0.0")]
#[diagnostic::on_unimplemented(
message = "cannot calculate the remainder of `{Self}` divided by `{Rhs}`",
label = "no implementation for `{Self} % {Rhs}`"
#[cfg_attr(
not(bootstrap),
diagnostic::on_unimplemented(
message = "cannot calculate the remainder of `{Self}` divided by `{Rhs}`",
label = "no implementation for `{Self} % {Rhs}`"
)
)]
#[doc(alias = "%")]
pub trait Rem<Rhs = Self> {
Expand Down Expand Up @@ -729,9 +738,12 @@ neg_impl! { isize i8 i16 i32 i64 i128 f32 f64 }
/// ```
#[lang = "add_assign"]
#[stable(feature = "op_assign_traits", since = "1.8.0")]
#[diagnostic::on_unimplemented(
message = "cannot add-assign `{Rhs}` to `{Self}`",
label = "no implementation for `{Self} += {Rhs}`"
#[cfg_attr(
not(bootstrap),
diagnostic::on_unimplemented(
message = "cannot add-assign `{Rhs}` to `{Self}`",
label = "no implementation for `{Self} += {Rhs}`"
)
)]
#[doc(alias = "+")]
#[doc(alias = "+=")]
Expand Down Expand Up @@ -796,9 +808,12 @@ add_assign_impl! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f32 f64 }
/// ```
#[lang = "sub_assign"]
#[stable(feature = "op_assign_traits", since = "1.8.0")]
#[diagnostic::on_unimplemented(
message = "cannot subtract-assign `{Rhs}` from `{Self}`",
label = "no implementation for `{Self} -= {Rhs}`"
#[cfg_attr(
not(bootstrap),
diagnostic::on_unimplemented(
message = "cannot subtract-assign `{Rhs}` from `{Self}`",
label = "no implementation for `{Self} -= {Rhs}`"
)
)]
#[doc(alias = "-")]
#[doc(alias = "-=")]
Expand Down Expand Up @@ -854,9 +869,12 @@ sub_assign_impl! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f32 f64 }
/// ```
#[lang = "mul_assign"]
#[stable(feature = "op_assign_traits", since = "1.8.0")]
#[diagnostic::on_unimplemented(
message = "cannot multiply-assign `{Self}` by `{Rhs}`",
label = "no implementation for `{Self} *= {Rhs}`"
#[cfg_attr(
not(bootstrap),
diagnostic::on_unimplemented(
message = "cannot multiply-assign `{Self}` by `{Rhs}`",
label = "no implementation for `{Self} *= {Rhs}`"
)
)]
#[doc(alias = "*")]
#[doc(alias = "*=")]
Expand Down Expand Up @@ -912,9 +930,12 @@ mul_assign_impl! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f32 f64 }
/// ```
#[lang = "div_assign"]
#[stable(feature = "op_assign_traits", since = "1.8.0")]
#[diagnostic::on_unimplemented(
message = "cannot divide-assign `{Self}` by `{Rhs}`",
label = "no implementation for `{Self} /= {Rhs}`"
#[cfg_attr(
not(bootstrap),
diagnostic::on_unimplemented(
message = "cannot divide-assign `{Self}` by `{Rhs}`",
label = "no implementation for `{Self} /= {Rhs}`"
)
)]
#[doc(alias = "/")]
#[doc(alias = "/=")]
Expand Down Expand Up @@ -973,9 +994,12 @@ div_assign_impl! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f32 f64 }
/// ```
#[lang = "rem_assign"]
#[stable(feature = "op_assign_traits", since = "1.8.0")]
#[diagnostic::on_unimplemented(
message = "cannot calculate and assign the remainder of `{Self}` divided by `{Rhs}`",
label = "no implementation for `{Self} %= {Rhs}`"
#[cfg_attr(
not(bootstrap),
diagnostic::on_unimplemented(
message = "cannot calculate and assign the remainder of `{Self}` divided by `{Rhs}`",
label = "no implementation for `{Self} %= {Rhs}`"
)
)]
#[doc(alias = "%")]
#[doc(alias = "%=")]
Expand Down

0 comments on commit 4353333

Please sign in to comment.