Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 111 additions & 0 deletions compiler/rustc_attr_ir/src/attribute_docs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -283,3 +283,114 @@ const _: () = ();
/// [#156920]:https://github.com/rust-lang/rust/issues/156920 "`dyn Allocator` together with `Allocator + Clone` requirements is unsound, leading to UB with `Arc`"
/// [#160045]:https://github.com/rust-lang/rust/issues/160045 "`iter::Rev`'s `TrustedLen` impl is unsound with trait objects"
const _: () = ();

#[doc(attribute = "rustc_on_unimplemented")]
/// Customize the error message when a trait is not implemented.
///
/// It must be used on the declaration of said trait.
///
/// # Syntax
///
/// ```grammar
/// RustcOnUnimplementedAttribute ->
/// rustc_on_unimplemented ( ( Directive ),+ )
///
/// Directive ->
/// on ( Filter, ( DirectiveOption ),+ )
/// | ( DirectiveOption ),*
///
/// DirectiveOption ->
/// message = STRING_LITERAL
/// | label = STRING_LITERAL
/// | note = STRING_LITERAL
///
/// Filter ->
/// FilterAll
/// | FilterAny
/// | FilterNot
/// | FilterOption
///
/// FilterAll ->
/// all ( ( Filter ),* )
///
/// FilterAny ->
/// any ( ( Filter ),* )
///
/// FilterNot ->
/// not ( Filter )
///
/// FilterOption ->
/// crate_local
/// | direct
/// | from_desugaring ( = STRING_LITERAL )?
/// | cause = STRING_LITERAL
/// | IDENTIFIER = STRING_LITERAL
///
/// ```
///
/// The following keys have the given meaning. At least one must be specified.
/// - `on` - filters the application of the attribute. See [#Filters](#filters).
/// - `message` — The text for the top level error message. May only be specified at most once.
/// - `label` — The text for the label shown inline in the broken code in the error message.
/// May only be specified at most once.
/// - `note` — Provides additional note(s)
///
/// `message`, `label`, and `note` are available with the [`diagnostic::on_unimplemented`]
/// attribute. If possible, use that instead.
///
/// # Example
///
#[doc = include_example!("rustc_on_unimplemented")]
///
/// # Filters
///
/// To allow more targeted error messages, it is possible to filter the
/// application of these keys with `on`.
///
/// You can filter on the following boolean flags:
/// - `crate_local`: whether the code causing the trait bound to not be
/// fulfilled is part of the user's crate.
/// This is used to avoid suggesting code changes that would require modifying a dependency.
/// - `direct`: whether this is a user-specified rather than derived obligation.
/// - `from_desugaring`: whether we are in some kind of desugaring, like `?`
/// or a `try` block for example.
/// This flag can also be matched on, see below.
///
/// You can match on the following names and values, using `name = "value"`:
/// - `cause`: Match against one variant of the `ObligationCauseCode` enum.
/// Only `"MainFunctionType"` is supported.
/// - `from_desugaring`: Match against a particular variant of the `DesugaringKind` enum.
/// The desugaring is identified by its variant name, for example
/// `"QuestionMark"` for `?` desugaring, or `"TryBlock"` for `try` blocks.
/// - `Self` and any generic arguments of the trait, like `Self = "alloc::string::String"`
/// or `Rhs="i32"`.
///
/// The compiler provides several values to match on, for example:
/// - the self_ty, pretty printed with and without type arguments resolved.
/// - `"{integral}"`, if self_ty is an integral of which the type is known.
/// - `"[]"`, `"[{ty}]"`, `"[{ty}; _]"`, `"[{ty}; $N]"` when applicable.
/// - references to said slices and arrays.
/// - `"fn"`, `"unsafe fn"` or `"#[target_feature] fn"` when self is a function.
/// - `"{integer}"` and `"{float}"` if the type is a number but we haven't inferred it yet.
/// - `"{struct}"`, `"{enum}"` and `"{union}"` to match self as an ADT
/// - combinations of the above, like `"[{integral}; _]"`.
///
#[doc = include_example!("rustc_on_unimplemented_filter")]
///
/// # Formatting
///
/// The string literals are format strings that accept parameters wrapped in braces -
/// positional and listed parameters are not accepted.
/// The following parameter names are valid:
/// - `Self` and all generic parameters of the trait.
/// - `This`: the name of the trait the attribute is on, without generics.
/// - `This:path`: the full path of the trait the attribute is on, with unresolved generics.
/// - `This:resolved`: the full path of the trait the attribute is on, with resolved generics.
/// Additionally, this will "sugar" the `Fn(...)` traits.
/// - `ItemContext`: the kind of `hir::Node` we're in, things like `"an async block"`,
/// `"a function"`, `"an async function"`, etc.
///
#[doc = include_example!("rustc_on_unimplemented_format")]
///
/// [`diagnostic::on_unimplemented`]: https://doc.rust-lang.org/nightly/reference/attributes/diagnostics.html#the-diagnosticon_unimplemented-attribute
const _: () = ();
205 changes: 17 additions & 188 deletions src/doc/rustc-dev-guide/src/diagnostics.md
Original file line number Diff line number Diff line change
Expand Up @@ -862,192 +862,21 @@ struct](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/json/struct
Don't confuse this with
[`errors::Diag`](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/struct.Diag.html)!

## `#[rustc_on_unimplemented]`

This attribute allows trait definitions to modify error messages when an implementation was
expected but not found.
The string literals in the attribute are format strings and can be formatted with named parameters.
See the Formatting section below for what parameters are permitted.

```rust,ignore
#[rustc_on_unimplemented(message = "an iterator over \
elements of type `{A}` cannot be built from a \
collection of type `{Self}`")]
trait MyIterator<A> {
fn next(&mut self) -> A;
}

fn iterate_chars<I: MyIterator<char>>(i: I) {
// ...
}

fn main() {
iterate_chars(&[1, 2, 3][..]);
}
```

When the user compiles this, they will see the following;

```txt
error[E0277]: an iterator over elements of type `char` cannot be built from a collection of type `&[{integer}]`
--> src/main.rs:13:19
|
13 | iterate_chars(&[1, 2, 3][..]);
| ------------- ^^^^^^^^^^^^^^ the trait `MyIterator<char>` is not implemented for `&[{integer}]`
| |
| required by a bound introduced by this call
|
note: required by a bound in `iterate_chars`
```

You can modify the contents of:
- the main error message (`message`)
- the label (`label`)
- the note(s) (`note`)

For example, the following attribute

```rust,ignore
#[rustc_on_unimplemented(message = "message", label = "label", note = "note")]
trait MyIterator<A> {
fn next(&mut self) -> A;
}
```

Would generate the following output:

```text
error[E0277]: message
--> <file>:10:19
|
10 | iterate_chars(&[1, 2, 3][..]);
| ------------- ^^^^^^^^^^^^^^ label
| |
| required by a bound introduced by this call
|
= help: the trait `MyIterator<char>` is not implemented for `&[{integer}]`
= note: note
note: required by a bound in `iterate_chars`
```

The functionality discussed so far is also available with
[`#[diagnostic::on_unimplemented]`](https://doc.rust-lang.org/nightly/reference/attributes/diagnostics.html#the-diagnosticon_unimplemented-attribute).
If you can, you should use that instead.

### Filtering

To allow more targeted error messages,
it is possible to filter the application of these fields with `on`.

You can filter on the following boolean flags:
- `crate_local`: whether the code causing the trait bound to not be
fulfilled is part of the user's crate.
This is used to avoid suggesting code changes that would require modifying a dependency.
- `direct`: whether this is a user-specified rather than derived obligation.
- `from_desugaring`: whether we are in some kind of desugaring,
like `?` or a `try` block for example.
This flag can also be matched on, see below.

You can match on the following names and values, using `name = "value"`:
- `cause`: Match against one variant of the `ObligationCauseCode` enum.
Only `"MainFunctionType"` is supported.
- `from_desugaring`: Match against a particular variant of the `DesugaringKind` enum.
The desugaring is identified by its variant name, for example
`"QuestionMark"` for `?` desugaring, or `"TryBlock"` for `try` blocks.
- `Self` and any generic arguments of the trait,
like `Self = "alloc::string::String"` or `Rhs="i32"`.

The compiler can provide several values to match on, for example:
- the self_ty, pretty printed with and without type arguments resolved.
- `"{integral}"`, if self_ty is an integral of which the type is known.
- `"[]"`, `"[{ty}]"`, `"[{ty}; _]"`, `"[{ty}; $N]"` when applicable.
- references to said slices and arrays.
- `"fn"`, `"unsafe fn"` or `"#[target_feature] fn"` when self is a function.
- `"{integer}"` and `"{float}"` if the type is a number but we haven't inferred it yet.
- `"{struct}"`, `"{enum}"` and `"{union}"` to match self as an ADT
- combinations of the above, like `"[{integral}; _]"`.

For example, the `Iterator` trait can be filtered in the following way:

```rust,ignore
#[rustc_on_unimplemented(
on(Self = "&str", note = "call `.chars()` or `.as_bytes()` on `{Self}`"),
message = "`{Self}` is not an iterator",
label = "`{Self}` is not an iterator",
note = "maybe try calling `.iter()` or a similar method"
)]
pub trait Iterator {}
```

Which would produce the following outputs:

```text
error[E0277]: `Foo` is not an iterator
--> src/main.rs:4:16
|
4 | for foo in Foo {}
| ^^^ `Foo` is not an iterator
|
= note: maybe try calling `.iter()` or a similar method
= help: the trait `std::iter::Iterator` is not implemented for `Foo`
= note: required by `std::iter::IntoIterator::into_iter`

error[E0277]: `&str` is not an iterator
--> src/main.rs:5:16
|
5 | for foo in "" {}
| ^^ `&str` is not an iterator
|
= note: call `.chars()` or `.bytes() on `&str`
= help: the trait `std::iter::Iterator` is not implemented for `&str`
= note: required by `std::iter::IntoIterator::into_iter`
```

The `on` filter accepts `all`, `any` and `not` predicates similar to the `cfg` attribute:

```rust,ignore
#[rustc_on_unimplemented(on(
all(Self = "&str", T = "alloc::string::String"),
note = "you can coerce a `{T}` into a `{Self}` by writing `&*variable`"
))]
pub trait From<T>: Sized {
/* ... */
}
```

### Formatting

The string literals are format strings that accept parameters wrapped in braces
but positional and listed parameters are not accepted.
The following parameter names are valid:
- `Self` and all generic parameters of the trait.
- `This`: the name of the trait the attribute is on, without generics.
- `This:path`: the full path of the trait the attribute is on, with unresolved generics.
- `This:resolved`: the full path of the trait the attribute is on, with resolved generics.
Additionally, this will "sugar" the `Fn(...)` traits.
- `ItemContext`: the kind of `hir::Node` we're in, things like `"an async block"`,
`"a function"`, `"an async function"`, etc.

Something like:

```rust,ignore
#![feature(rustc_attrs)]

#[rustc_on_unimplemented(message = "Self = `{Self}`, \
T = `{T}`, this = `{This}`, trait = `{Trait}`, \
context = `{ItemContext}`")]
pub trait From<T>: Sized {
fn from(x: T) -> Self;
}

fn main() {
let x: i8 = From::from(42_i32);
}
```

Will format the message into
```text
"Self = `i8`, T = `i32`, this = `From`, trait = `From<i32>`, context = `a function`"
```

## Diagnostic attributes

There are many attributes that can be used to generate or improve error messages:
- `diagnostic::on_unimplemented` and [`rustc_on_unimplemented`]: customize unimplemented trait errors
- `diagnostic::on_move`: customize borrowcheck errors
- `diagnostic::on_unknown`: customize unresolved imports errors
- `diagnostic::on_unmatched_args`: customize macro matcher errors
- `diagnostic::opaque`: stop the internals of macros from showing up in error messages
- `diagnostic::on_const`: customize non-const trait impl errors
- `rustc_as_ptr`: used by the `dangling_pointers_from_temporaries` lint.
- `rustc_never_returns_null_ptr`: used by the `useless_ptr_null_checks` lint.
- `rustc_no_implicit_autorefs`: used for the `dangerous_implicit_autorefs` lint.

If possible, consider improving or implementing such an attribute rather than adding custom error
reporting code.

[`rustc_on_unimplemented`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_attr_ir/attribute.rustc_on_unimplemented.html
[diag]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/struct.Diag.html
16 changes: 16 additions & 0 deletions tests/ui/attributes/doc_examples/rustc_on_unimplemented.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//@ dont-require-annotations: ERROR
//@ compile-flags: --crate-type lib -Z ui-testing=no

#![feature(rustc_attrs)]

#[rustc_on_unimplemented(
message = "cannot add `{Rhs}` to `{Self}`",
label = "no implementation for `{Self} + {Rhs}`",
)]
pub trait MyAdd<Rhs = Self> {
fn add(self, rhs: Rhs) -> Self;
}

fn main() {
MyAdd::add(42_u8, 42.0);
}
18 changes: 18 additions & 0 deletions tests/ui/attributes/doc_examples/rustc_on_unimplemented.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
error[E0277]: cannot add `_` to `u8`
--> $DIR/rustc_on_unimplemented.rs:15:16
|
15 | MyAdd::add(42_u8, 42.0);
| ---------- ^^^^^ no implementation for `u8 + _`
| |
| required by a bound introduced by this call
|
= help: the trait `MyAdd<_>` is not implemented for `u8`
help: this trait has no implementations, consider adding one
--> $DIR/rustc_on_unimplemented.rs:10:1
|
10 | pub trait MyAdd<Rhs = Self> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0277`.
18 changes: 18 additions & 0 deletions tests/ui/attributes/doc_examples/rustc_on_unimplemented_filter.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//@ dont-require-annotations: ERROR
//@ compile-flags: --crate-type lib -Z ui-testing=no

#![feature(rustc_attrs)]

#[rustc_on_unimplemented(
on(all(Self = "{integer}", Rhs = "{float}"), message = "cannot add a float to an integer",),
on(all(Self = "{float}", Rhs = "{integer}"), message = "cannot add an integer to a float",),
message = "cannot add `{Rhs}` to `{Self}`",
label = "no implementation for `{Self} + {Rhs}`"
)]
pub trait MyAdd<Rhs = Self> {
fn add(self, rhs: Rhs) -> Self;
}

fn main() {
MyAdd::add(42_u8, 42.0);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
error[E0277]: cannot add `_` to `u8`
--> $DIR/rustc_on_unimplemented_filter.rs:17:16
|
17 | MyAdd::add(42_u8, 42.0);
| ---------- ^^^^^ no implementation for `u8 + _`
| |
| required by a bound introduced by this call
|
= help: the trait `MyAdd<_>` is not implemented for `u8`
help: this trait has no implementations, consider adding one
--> $DIR/rustc_on_unimplemented_filter.rs:12:1
|
12 | pub trait MyAdd<Rhs = Self> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0277`.
Loading
Loading