Skip to content

Commit 6dc2f38

Browse files
Centrilmark-i-m
authored andcommitted
Explain rustc_on_unimplemented
1 parent 51a4a72 commit 6dc2f38

File tree

1 file changed

+147
-0
lines changed

1 file changed

+147
-0
lines changed

src/diagnostics.md

+147
Original file line numberDiff line numberDiff line change
@@ -342,3 +342,150 @@ The JSON emitter defines [its own `Diagnostic`
342342
struct](https://github.com/rust-lang/rust/blob/b2c6b8c29f13f8d1f242da89e587960b95337819/src/libsyntax/json.rs#L85-L99)
343343
(and sub-structs) for the JSON serialization. Don't confuse this with
344344
[`errors::Diagnostic`](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/struct.Diagnostic.html)!
345+
346+
## `#[rustc_on_unimplemented(...)]`
347+
348+
The `#[rustc_on_unimplemented]` attribute allows trait definitions to add specialized
349+
notes to error messages when an implementation was expected but not found.
350+
You can refer to the trait's generic arguments by name and to the resolved type using `Self`.
351+
352+
For example:
353+
354+
```rust,ignore
355+
#![feature(rustc_attrs)]
356+
357+
#[rustc_on_unimplemented="an iterator over elements of type `{A}` \
358+
cannot be built from a collection of type `{Self}`"]
359+
trait MyIterator<A> {
360+
fn next(&mut self) -> A;
361+
}
362+
363+
fn iterate_chars<I: MyIterator<char>>(i: I) {
364+
// ...
365+
}
366+
367+
fn main() {
368+
iterate_chars(&[1, 2, 3][..]);
369+
}
370+
```
371+
372+
When the user compiles this, they will see the following;
373+
374+
```txt
375+
error[E0277]: the trait bound `&[{integer}]: MyIterator<char>` is not satisfied
376+
--> <anon>:14:5
377+
|
378+
14 | iterate_chars(&[1, 2, 3][..]);
379+
| ^^^^^^^^^^^^^ an iterator over elements of type `char` cannot be built from a collection of type `&[{integer}]`
380+
|
381+
= help: the trait `MyIterator<char>` is not implemented for `&[{integer}]`
382+
= note: required by `iterate_chars`
383+
```
384+
385+
`rustc_on_unimplemented` also supports advanced filtering for better targeting
386+
of messages, as well as modifying specific parts of the error message. You
387+
target the text of:
388+
389+
- the main error message (`message`)
390+
- the label (`label`)
391+
- an extra note (`note`)
392+
393+
For example, the following attribute
394+
395+
```rust,ignore
396+
#[rustc_on_unimplemented(
397+
message="message",
398+
label="label",
399+
note="note"
400+
)]
401+
trait MyIterator<A> {
402+
fn next(&mut self) -> A;
403+
}
404+
```
405+
406+
Would generate the following output:
407+
408+
```text
409+
error[E0277]: message
410+
--> <anon>:14:5
411+
|
412+
14 | iterate_chars(&[1, 2, 3][..]);
413+
| ^^^^^^^^^^^^^ label
414+
|
415+
= note: note
416+
= help: the trait `MyIterator<char>` is not implemented for `&[{integer}]`
417+
= note: required by `iterate_chars`
418+
```
419+
420+
To allow more targeted error messages, it is possible to filter the
421+
application of these fields based on a variety of attributes when using
422+
`on`:
423+
424+
- `crate_local`: whether the code causing the trait bound to not be
425+
fulfilled is part of the user's crate. This is used to avoid suggesting
426+
code changes that would require modifying a dependency.
427+
- Any of the generic arguments that can be substituted in the text can be
428+
referred by name as well for filtering, like `Rhs="i32"`, except for
429+
`Self`.
430+
- `_Self`: to filter only on a particular calculated trait resolution, like
431+
`Self="std::iter::Iterator<char>"`. This is needed because `Self` is a
432+
keyword which cannot appear in attributes.
433+
- `direct`: user-specified rather than derived obligation.
434+
- `from_method`: usable both as boolean (whether the flag is present, like
435+
`crate_local`) or matching against a particular method. Currently used
436+
for `try`.
437+
- `from_desugaring`: usable both as boolean (whether the flag is present)
438+
or matching against a particular desugaring. The desugaring is identified
439+
with its variant name in the `DesugaringKind` enum.
440+
441+
For example, the `Iterator` trait can be annotated in the following way:
442+
443+
```rust,ignore
444+
#[rustc_on_unimplemented(
445+
on(
446+
_Self="&str",
447+
note="call `.chars()` or `.as_bytes()` on `{Self}"
448+
),
449+
message="`{Self}` is not an iterator",
450+
label="`{Self}` is not an iterator",
451+
note="maybe try calling `.iter()` or a similar method"
452+
)]
453+
pub trait Iterator {}
454+
```
455+
456+
Which would produce the following outputs:
457+
458+
```text
459+
error[E0277]: `Foo` is not an iterator
460+
--> src/main.rs:4:16
461+
|
462+
4 | for foo in Foo {}
463+
| ^^^ `Foo` is not an iterator
464+
|
465+
= note: maybe try calling `.iter()` or a similar method
466+
= help: the trait `std::iter::Iterator` is not implemented for `Foo`
467+
= note: required by `std::iter::IntoIterator::into_iter`
468+
469+
error[E0277]: `&str` is not an iterator
470+
--> src/main.rs:5:16
471+
|
472+
5 | for foo in "" {}
473+
| ^^ `&str` is not an iterator
474+
|
475+
= note: call `.chars()` or `.bytes() on `&str`
476+
= help: the trait `std::iter::Iterator` is not implemented for `&str`
477+
= note: required by `std::iter::IntoIterator::into_iter`
478+
```
479+
480+
If you need to filter on multiple attributes, you can use `all`, `any` or
481+
`not` in the following way:
482+
483+
```rust,ignore
484+
#[rustc_on_unimplemented(
485+
on(
486+
all(_Self="&str", T="std::string::String"),
487+
note="you can coerce a `{T}` into a `{Self}` by writing `&*variable`"
488+
)
489+
)]
490+
pub trait From<T>: Sized { /* ... */ }
491+
```

0 commit comments

Comments
 (0)