Skip to content

Commit

Permalink
Rollup merge of rust-lang#132056 - weiznich:diagnostic_do_not_recomme…
Browse files Browse the repository at this point in the history
…nd_final_tests, r=compiler-errors

Stabilize `#[diagnostic::do_not_recommend]`

This PR seeks to stabilize the `#[diagnostic::do_not_recommend]`attribute.

This attribute was first proposed as `#[do_not_recommend`] attribute in RFC 2397 (rust-lang/rfcs#2397). It gives the crate authors the ability to not suggest to the compiler to not show certain traits in its error messages.

With the presence of the `#[diagnostic]` tool attribute namespace it was decided to move the attribute there, as that lowers the amount of guarantees the compiler needs to give about the exact way this influences error messages. It turns the attribute into a hint which can be ignored. In addition to the original proposed functionality this attribute now also hides the marked trait in help messages ("This trait is implemented by: ").

The attribute does not accept any argument and can only be placed on trait implementations. If it is placed somewhere else a lint warning is emitted and the attribute is otherwise ignored. If an argument is detected a lint warning is emitted and the argument is ignored. This follows the rules outlined by the diagnostic namespace.

This attribute allows crates like diesel to improve their error messages drastically. The most common example here is the following error message:

```
error[E0277]: the trait bound `&str: Expression` is not satisfied
  --> /home/weiznich/Documents/rust/rust/tests/ui/diagnostic_namespace/do_not_recommend.rs:53:15
   |
LL |     SelectInt.check("bar");
   |               ^^^^^ the trait `Expression` is not implemented for `&str`, which is required by `&str: AsExpression<Integer>`
   |
   = help: the following other types implement trait `Expression`:
             Bound<T>
             SelectInt
note: required for `&str` to implement `AsExpression<Integer>`
  --> /home/weiznich/Documents/rust/rust/tests/ui/diagnostic_namespace/do_not_recommend.rs:26:13
   |
LL | impl<T, ST> AsExpression<ST> for T
   |             ^^^^^^^^^^^^^^^^     ^
LL | where
LL |     T: Expression<SqlType = ST>,
   |        ------------------------ unsatisfied trait bound introduced here
```

By applying the new attribute to the wild card trait implementation of
`AsExpression` for `T: Expression` the error message becomes:

```
error[E0277]: the trait bound `&str: AsExpression<Integer>` is not satisfied
  --> $DIR/as_expression.rs:55:15
   |
LL |     SelectInt.check("bar");
   |               ^^^^^ the trait `AsExpression<Integer>` is not implemented for `&str`
   |
   = help: the trait `AsExpression<Text>` is implemented for `&str`
   = help: for that trait implementation, expected `Text`, found `Integer`
```

which makes it much easier for users to understand that they are facing a type mismatch.

Other explored example usages include:

* This standard library error message: rust-lang#128008
* That bevy derived example:
https://github.com/rust-lang/rust/blob/e1f306899514ea80abc1d1c9f6a57762afb304a3/tests/ui/diagnostic_namespace/do_not_recommend/supress_suggestions_in_help.rs (No
more tuple pyramids)

Fixes rust-lang#51992

r? ``@compiler-errors``

This PR also adds a few more tests, makes sure that all the tests are run for the old and new trait solver and adds a check that the attribute does not contain arguments.
  • Loading branch information
GuillaumeGomez authored Dec 16, 2024
2 parents 83ab648 + fad597f commit 593de5e
Show file tree
Hide file tree
Showing 41 changed files with 300 additions and 152 deletions.
2 changes: 2 additions & 0 deletions compiler/rustc_feature/src/accepted.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,8 @@ declare_features! (
(accepted, destructuring_assignment, "1.59.0", Some(71126)),
/// Allows using the `#[diagnostic]` attribute tool namespace
(accepted, diagnostic_namespace, "1.78.0", Some(111996)),
/// Controls errors in trait implementations.
(accepted, do_not_recommend, "CURRENT_RUSTC_VERSION", Some(51992)),
/// Allows `#[doc(alias = "...")]`.
(accepted, doc_alias, "1.48.0", Some(50146)),
/// Allows `..` in tuple (struct) patterns.
Expand Down
5 changes: 2 additions & 3 deletions compiler/rustc_feature/src/builtin_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1188,10 +1188,9 @@ pub static BUILTIN_ATTRIBUTE_MAP: LazyLock<FxHashMap<Symbol, &BuiltinAttribute>>
map
});

pub fn is_stable_diagnostic_attribute(sym: Symbol, features: &Features) -> bool {
pub fn is_stable_diagnostic_attribute(sym: Symbol, _features: &Features) -> bool {
match sym {
sym::on_unimplemented => true,
sym::do_not_recommend => features.do_not_recommend(),
sym::on_unimplemented | sym::do_not_recommend => true,
_ => false,
}
}
2 changes: 0 additions & 2 deletions compiler/rustc_feature/src/unstable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -466,8 +466,6 @@ declare_features! (
(unstable, deprecated_suggestion, "1.61.0", Some(94785)),
/// Allows deref patterns.
(incomplete, deref_patterns, "1.79.0", Some(87121)),
/// Controls errors in trait implementations.
(unstable, do_not_recommend, "1.67.0", Some(51992)),
/// Tells rustdoc to automatically generate `#[doc(cfg(...))]`.
(unstable, doc_auto_cfg, "1.58.0", Some(43781)),
/// Allows `#[doc(cfg(...))]`.
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_passes/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,9 @@ passes_ignored_derived_impls =
passes_implied_feature_not_exist =
feature `{$implied_by}` implying `{$feature}` does not exist
passes_incorrect_do_not_recommend_args =
`#[diagnostic::do_not_recommend]` does not expect any arguments
passes_incorrect_do_not_recommend_location =
`#[diagnostic::do_not_recommend]` can only be placed on trait implementations
Expand Down
27 changes: 24 additions & 3 deletions compiler/rustc_passes/src/check_attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
for attr in attrs {
match attr.path().as_slice() {
[sym::diagnostic, sym::do_not_recommend, ..] => {
self.check_do_not_recommend(attr.span, hir_id, target)
self.check_do_not_recommend(attr.span, hir_id, target, attr, item)
}
[sym::diagnostic, sym::on_unimplemented, ..] => {
self.check_diagnostic_on_unimplemented(attr.span, hir_id, target)
Expand Down Expand Up @@ -349,15 +349,36 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
}

/// Checks if `#[diagnostic::do_not_recommend]` is applied on a trait impl.
fn check_do_not_recommend(&self, attr_span: Span, hir_id: HirId, target: Target) {
if !matches!(target, Target::Impl) {
fn check_do_not_recommend(
&self,
attr_span: Span,
hir_id: HirId,
target: Target,
attr: &Attribute,
item: Option<ItemLike<'_>>,
) {
if !matches!(target, Target::Impl)
|| matches!(
item,
Some(ItemLike::Item(hir::Item { kind: hir::ItemKind::Impl(_impl),.. }))
if _impl.of_trait.is_none()
)
{
self.tcx.emit_node_span_lint(
UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
hir_id,
attr_span,
errors::IncorrectDoNotRecommendLocation,
);
}
if !matches!(attr.meta_kind(), Some(MetaItemKind::Word)) {
self.tcx.emit_node_span_lint(
UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
hir_id,
attr_span,
errors::DoNotRecommendDoesNotExpectArgs,
);
}
}

/// Checks if `#[diagnostic::on_unimplemented]` is applied to a trait definition
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_passes/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ use crate::lang_items::Duplicate;
#[diag(passes_incorrect_do_not_recommend_location)]
pub(crate) struct IncorrectDoNotRecommendLocation;

#[derive(LintDiagnostic)]
#[diag(passes_incorrect_do_not_recommend_args)]
pub(crate) struct DoNotRecommendDoesNotExpectArgs;

#[derive(Diagnostic)]
#[diag(passes_autodiff_attr)]
pub(crate) struct AutoDiffAttr {
Expand Down
3 changes: 1 addition & 2 deletions compiler/rustc_resolve/src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -689,8 +689,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
&& let [namespace, attribute, ..] = &*path.segments
&& namespace.ident.name == sym::diagnostic
&& !(attribute.ident.name == sym::on_unimplemented
|| (attribute.ident.name == sym::do_not_recommend
&& self.tcx.features().do_not_recommend()))
|| attribute.ident.name == sym::do_not_recommend)
{
let distance =
edit_distance(attribute.ident.name.as_str(), sym::on_unimplemented.as_str(), 5);
Expand Down
2 changes: 1 addition & 1 deletion library/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,13 +107,13 @@
//
// Library features:
// tidy-alphabetical-start
#![cfg_attr(bootstrap, feature(do_not_recommend))]
#![feature(array_ptr_get)]
#![feature(asm_experimental_arch)]
#![feature(const_eval_select)]
#![feature(const_typed_swap)]
#![feature(core_intrinsics)]
#![feature(coverage_attribute)]
#![feature(do_not_recommend)]
#![feature(internal_impls_macro)]
#![feature(ip)]
#![feature(is_ascii_octdigit)]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error[E0277]: the trait bound `&str: AsExpression<Integer>` is not satisfied
--> $DIR/as_expression.rs:57:15
--> $DIR/as_expression.rs:55:15
|
LL | SelectInt.check("bar");
| ^^^^^ the trait `AsExpression<Integer>` is not implemented for `&str`
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error[E0277]: the trait bound `&str: AsExpression<<SelectInt as Expression>::SqlType>` is not satisfied
--> $DIR/as_expression.rs:57:21
--> $DIR/as_expression.rs:55:21
|
LL | SelectInt.check("bar");
| ----- ^^^^^ the trait `AsExpression<<SelectInt as Expression>::SqlType>` is not implemented for `&str`
Expand All @@ -8,7 +8,7 @@ LL | SelectInt.check("bar");
|
= help: the trait `AsExpression<Text>` is implemented for `&str`
note: required by a bound in `Foo::check`
--> $DIR/as_expression.rs:48:12
--> $DIR/as_expression.rs:46:12
|
LL | fn check<T>(&self, _: T) -> <T as AsExpression<<Self as Expression>::SqlType>>::Expression
| ----- required by a bound in this associated function
Expand All @@ -17,7 +17,7 @@ LL | T: AsExpression<Self::SqlType>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Foo::check`

error[E0277]: the trait bound `&str: AsExpression<Integer>` is not satisfied
--> $DIR/as_expression.rs:57:15
--> $DIR/as_expression.rs:55:15
|
LL | SelectInt.check("bar");
| ^^^^^ the trait `AsExpression<Integer>` is not implemented for `&str`
Expand All @@ -27,7 +27,7 @@ LL | SelectInt.check("bar");
= help: for that trait implementation, expected `Text`, found `Integer`

error[E0271]: type mismatch resolving `<SelectInt as Expression>::SqlType == Text`
--> $DIR/as_expression.rs:57:5
--> $DIR/as_expression.rs:55:5
|
LL | SelectInt.check("bar");
| ^^^^^^^^^^^^^^^^^^^^^^ expected `Text`, found `Integer`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
//@ ignore-compare-mode-next-solver (explicit revisions)
//@[next] compile-flags: -Znext-solver

#![feature(do_not_recommend)]

pub trait Expression {
type SqlType;
}
Expand Down

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
warning: `#[diagnostic::do_not_recommend]` does not expect any arguments
--> $DIR/does_not_acccept_args.rs:10:1
|
LL | #[diagnostic::do_not_recommend(not_accepted)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(unknown_or_malformed_diagnostic_attributes)]` on by default

warning: `#[diagnostic::do_not_recommend]` does not expect any arguments
--> $DIR/does_not_acccept_args.rs:14:1
|
LL | #[diagnostic::do_not_recommend(not_accepted = "foo")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

warning: `#[diagnostic::do_not_recommend]` does not expect any arguments
--> $DIR/does_not_acccept_args.rs:18:1
|
LL | #[diagnostic::do_not_recommend(not_accepted(42))]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

warning: 3 warnings emitted

Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
warning: `#[diagnostic::do_not_recommend]` does not expect any arguments
--> $DIR/does_not_acccept_args.rs:10:1
|
LL | #[diagnostic::do_not_recommend(not_accepted)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(unknown_or_malformed_diagnostic_attributes)]` on by default

warning: `#[diagnostic::do_not_recommend]` does not expect any arguments
--> $DIR/does_not_acccept_args.rs:14:1
|
LL | #[diagnostic::do_not_recommend(not_accepted = "foo")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

warning: `#[diagnostic::do_not_recommend]` does not expect any arguments
--> $DIR/does_not_acccept_args.rs:18:1
|
LL | #[diagnostic::do_not_recommend(not_accepted(42))]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

warning: 3 warnings emitted

Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//@ check-pass
//@ revisions: current next
//@ ignore-compare-mode-next-solver (explicit revisions)
//@[next] compile-flags: -Znext-solver

trait Foo {}
trait Bar {}
trait Baz {}

#[diagnostic::do_not_recommend(not_accepted)]
//~^ WARNING `#[diagnostic::do_not_recommend]` does not expect any arguments
impl<T> Foo for T where T: Send {}

#[diagnostic::do_not_recommend(not_accepted = "foo")]
//~^ WARNING `#[diagnostic::do_not_recommend]` does not expect any arguments
impl<T> Bar for T where T: Send {}

#[diagnostic::do_not_recommend(not_accepted(42))]
//~^ WARNING `#[diagnostic::do_not_recommend]` does not expect any arguments
impl<T> Baz for T where T: Send {}

fn main() {}

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,52 +1,58 @@
warning: `#[diagnostic::do_not_recommend]` can only be placed on trait implementations
--> $DIR/incorrect-locations.rs:4:1
--> $DIR/incorrect-locations.rs:6:1
|
LL | #[diagnostic::do_not_recommend]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(unknown_or_malformed_diagnostic_attributes)]` on by default

warning: `#[diagnostic::do_not_recommend]` can only be placed on trait implementations
--> $DIR/incorrect-locations.rs:8:1
--> $DIR/incorrect-locations.rs:10:1
|
LL | #[diagnostic::do_not_recommend]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

warning: `#[diagnostic::do_not_recommend]` can only be placed on trait implementations
--> $DIR/incorrect-locations.rs:12:1
--> $DIR/incorrect-locations.rs:14:1
|
LL | #[diagnostic::do_not_recommend]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

warning: `#[diagnostic::do_not_recommend]` can only be placed on trait implementations
--> $DIR/incorrect-locations.rs:16:1
--> $DIR/incorrect-locations.rs:18:1
|
LL | #[diagnostic::do_not_recommend]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

warning: `#[diagnostic::do_not_recommend]` can only be placed on trait implementations
--> $DIR/incorrect-locations.rs:20:1
--> $DIR/incorrect-locations.rs:22:1
|
LL | #[diagnostic::do_not_recommend]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

warning: `#[diagnostic::do_not_recommend]` can only be placed on trait implementations
--> $DIR/incorrect-locations.rs:24:1
--> $DIR/incorrect-locations.rs:26:1
|
LL | #[diagnostic::do_not_recommend]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

warning: `#[diagnostic::do_not_recommend]` can only be placed on trait implementations
--> $DIR/incorrect-locations.rs:28:1
--> $DIR/incorrect-locations.rs:30:1
|
LL | #[diagnostic::do_not_recommend]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

warning: `#[diagnostic::do_not_recommend]` can only be placed on trait implementations
--> $DIR/incorrect-locations.rs:32:1
--> $DIR/incorrect-locations.rs:34:1
|
LL | #[diagnostic::do_not_recommend]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

warning: 8 warnings emitted
warning: `#[diagnostic::do_not_recommend]` can only be placed on trait implementations
--> $DIR/incorrect-locations.rs:38:1
|
LL | #[diagnostic::do_not_recommend]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

warning: 9 warnings emitted

Loading

0 comments on commit 593de5e

Please sign in to comment.