gate rustc_on_unimplemented under rustc_attrs

This commit is contained in:
Mazdak Farrokhzad 2019-10-25 07:19:07 +02:00
parent e4931eaaa3
commit 1c7595fd0f
17 changed files with 34 additions and 190 deletions

View File

@ -1,154 +0,0 @@
# `on_unimplemented`
The tracking issue for this feature is: [#29628]
[#29628]: https://github.com/rust-lang/rust/issues/29628
------------------------
The `on_unimplemented` feature provides the `#[rustc_on_unimplemented]`
attribute, which allows trait definitions to add specialized notes to error
messages when an implementation was expected but not found. You can refer
to the trait's generic arguments by name and to the resolved type using
`Self`.
For example:
```rust,compile_fail
#![feature(on_unimplemented)]
#[rustc_on_unimplemented="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]: the trait bound `&[{integer}]: MyIterator<char>` is not satisfied
--> <anon>:14:5
|
14 | iterate_chars(&[1, 2, 3][..]);
| ^^^^^^^^^^^^^ an iterator over elements of type `char` cannot be built from a collection of type `&[{integer}]`
|
= help: the trait `MyIterator<char>` is not implemented for `&[{integer}]`
= note: required by `iterate_chars`
```
`on_unimplemented` also supports advanced filtering for better targeting
of messages, as well as modifying specific parts of the error message. You
target the text of:
- the main error message (`message`)
- the label (`label`)
- an extra note (`note`)
For example, the following attribute
```rust,compile_fail
#[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
--> <anon>:14:5
|
14 | iterate_chars(&[1, 2, 3][..]);
| ^^^^^^^^^^^^^ label
|
= note: note
= help: the trait `MyIterator<char>` is not implemented for `&[{integer}]`
= note: required by `iterate_chars`
```
To allow more targeted error messages, it is possible to filter the
application of these fields based on a variety of attributes when using
`on`:
- `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.
- Any of the generic arguments that can be substituted in the text can be
referred by name as well for filtering, like `Rhs="i32"`, except for
`Self`.
- `_Self`: to filter only on a particular calculated trait resolution, like
`Self="std::iter::Iterator<char>"`. This is needed because `Self` is a
keyword which cannot appear in attributes.
- `direct`: user-specified rather than derived obligation.
- `from_method`: usable both as boolean (whether the flag is present, like
`crate_local`) or matching against a particular method. Currently used
for `try`.
- `from_desugaring`: usable both as boolean (whether the flag is present)
or matching against a particular desugaring. The desugaring is identified
with its variant name in the `DesugaringKind` enum.
For example, the `Iterator` trait can be annotated in the following way:
```rust,compile_fail
#[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`
```
If you need to filter on multiple attributes, you can use `all`, `any` or
`not` in the following way:
```rust,compile_fail
#[rustc_on_unimplemented(
on(
all(_Self="&str", T="std::string::String"),
note="you can coerce a `{T}` into a `{Self}` by writing `&*variable`"
)
)]
pub trait From<T>: Sized { /* ... */ }
```

View File

@ -116,7 +116,7 @@
#![feature(unsize)]
#![feature(unsized_locals)]
#![feature(allocator_internals)]
#![feature(on_unimplemented)]
#![cfg_attr(bootstrap, feature(on_unimplemented))]
#![feature(rustc_const_unstable)]
#![feature(slice_partition_dedup)]
#![feature(maybe_uninit_extra, maybe_uninit_slice)]

View File

@ -89,7 +89,7 @@
#![feature(nll)]
#![feature(exhaustive_patterns)]
#![feature(no_core)]
#![feature(on_unimplemented)]
#![cfg_attr(bootstrap, feature(on_unimplemented))]
#![feature(optin_builtin_traits)]
#![feature(prelude_import)]
#![feature(repr_simd, platform_intrinsics)]

View File

@ -607,7 +607,7 @@ position that needs that trait. For example, when the following code is
compiled:
```compile_fail
#![feature(on_unimplemented)]
#![feature(rustc_attrs)]
fn foo<T: Index<u8>>(x: T){}
@ -639,7 +639,7 @@ position that needs that trait. For example, when the following code is
compiled:
```compile_fail
#![feature(on_unimplemented)]
#![feature(rustc_attrs)]
fn foo<T: Index<u8>>(x: T){}
@ -669,7 +669,7 @@ position that needs that trait. For example, when the following code is
compiled:
```compile_fail
#![feature(on_unimplemented)]
#![feature(rustc_attrs)]
fn foo<T: Index<u8>>(x: T){}

View File

@ -284,7 +284,7 @@
#![feature(never_type)]
#![feature(nll)]
#![cfg_attr(bootstrap, feature(non_exhaustive))]
#![feature(on_unimplemented)]
#![cfg_attr(bootstrap, feature(on_unimplemented))]
#![feature(optin_builtin_traits)]
#![feature(panic_info_message)]
#![feature(panic_internals)]

View File

@ -134,9 +134,6 @@ declare_features! (
/// Allows using `rustc_*` attributes (RFC 572).
(active, rustc_attrs, "1.0.0", Some(29642), None),
/// Allows using `#[on_unimplemented(..)]` on traits.
(active, on_unimplemented, "1.0.0", Some(29628), None),
/// Allows using the `box $expr` syntax.
(active, box_syntax, "1.0.0", Some(49733), None),

View File

@ -166,7 +166,7 @@ macro_rules! experimental {
}
const IMPL_DETAIL: &str = "internal implementation detail";
const INTERAL_UNSTABLE: &str = "this is an internal attribute that will never be stable";
const INTERNAL_UNSTABLE: &str = "this is an internal attribute that will never be stable";
pub type BuiltinAttribute = (Symbol, AttributeType, AttributeTemplate, AttributeGate);
@ -418,14 +418,14 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
linkage, Whitelisted, template!(NameValueStr: "external|internal|..."),
"the `linkage` attribute is experimental and not portable across platforms",
),
rustc_attr!(rustc_std_internal_symbol, Whitelisted, template!(Word), INTERAL_UNSTABLE),
rustc_attr!(rustc_std_internal_symbol, Whitelisted, template!(Word), INTERNAL_UNSTABLE),
// ==========================================================================
// Internal attributes, Macro related:
// ==========================================================================
rustc_attr!(rustc_builtin_macro, Whitelisted, template!(Word), IMPL_DETAIL),
rustc_attr!(rustc_proc_macro_decls, Normal, template!(Word), INTERAL_UNSTABLE),
rustc_attr!(rustc_proc_macro_decls, Normal, template!(Word), INTERNAL_UNSTABLE),
rustc_attr!(
rustc_macro_transparency, Whitelisted,
template!(NameValueStr: "transparent|semitransparent|opaque"),
@ -436,17 +436,16 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
// Internal attributes, Diagnostics related:
// ==========================================================================
gated!(
rustc_attr!(
rustc_on_unimplemented, Whitelisted,
template!(
List: r#"/*opt*/ message = "...", /*opt*/ label = "...", /*opt*/ note = "...""#,
NameValueStr: "message"
),
on_unimplemented,
experimental!(rustc_on_unimplemented),
INTERNAL_UNSTABLE
),
// Whitelists "identity-like" conversion methods to suggest on type mismatch.
rustc_attr!(rustc_conversion_suggestion, Whitelisted, template!(Word), INTERAL_UNSTABLE),
rustc_attr!(rustc_conversion_suggestion, Whitelisted, template!(Word), INTERNAL_UNSTABLE),
// ==========================================================================
// Internal attributes, Const related:
@ -454,7 +453,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
rustc_attr!(rustc_promotable, Whitelisted, template!(Word), IMPL_DETAIL),
rustc_attr!(rustc_allow_const_fn_ptr, Whitelisted, template!(Word), IMPL_DETAIL),
rustc_attr!(rustc_args_required_const, Whitelisted, template!(List: "N"), INTERAL_UNSTABLE),
rustc_attr!(rustc_args_required_const, Whitelisted, template!(List: "N"), INTERNAL_UNSTABLE),
// ==========================================================================
// Internal attributes, Layout related:

View File

@ -99,6 +99,9 @@ declare_features! (
/// + `__register_diagnostic`
/// +`__build_diagnostic_array`
(removed, rustc_diagnostic_macros, "1.38.0", None, None, None),
/// Allows using `#[on_unimplemented(..)]` on traits.
/// (Moved to `rustc_attrs`.)
(removed, on_unimplemented, "1.40.0", None, None, None),
// -------------------------------------------------------------------------
// feature-group-end: removed features

View File

@ -5,7 +5,7 @@
// revisions: cfail1 cfail2
// build-pass (FIXME(62277): could be check-pass?)
#![feature(on_unimplemented)]
#![feature(rustc_attrs)]
#![deny(unused_attributes)]
#[rustc_on_unimplemented = "invalid"]

View File

@ -1,9 +0,0 @@
// Test that `#[rustc_on_unimplemented]` is gated by `on_unimplemented` feature
// gate.
#[rustc_on_unimplemented = "test error `{Self}` with `{Bar}`"]
//~^ ERROR the `#[rustc_on_unimplemented]` attribute is an experimental feature
trait Foo<Bar>
{}
fn main() {}

View File

@ -1,6 +1,6 @@
// ignore-tidy-linelength
#![feature(on_unimplemented)]
#![feature(rustc_attrs)]
#![allow(unused)]

View File

@ -2,7 +2,7 @@
// access to the variable, whether that mutable access be used
// for direct assignment or for taking mutable ref. Issue #6801.
#![feature(on_unimplemented)]
#![feature(rustc_attrs)]
#[rustc_on_unimplemented(
message="the message"

View File

@ -0,0 +1,8 @@
// 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<Bar>
{}
fn main() {}

View File

@ -1,11 +1,11 @@
error[E0658]: the `#[rustc_on_unimplemented]` attribute is an experimental feature
--> $DIR/feature-gate-on-unimplemented.rs:4:1
error[E0658]: this is an internal attribute that will never be stable
--> $DIR/feature-gate-on-unimplemented.rs:3:1
|
LL | #[rustc_on_unimplemented = "test error `{Self}` with `{Bar}`"]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: for more information, see https://github.com/rust-lang/rust/issues/29628
= help: add `#![feature(on_unimplemented)]` to the crate attributes to enable
= note: for more information, see https://github.com/rust-lang/rust/issues/29642
= help: add `#![feature(rustc_attrs)]` to the crate attributes to enable
error: aborting due to previous error

View File

@ -1,6 +1,6 @@
// Test if the on_unimplemented message override works
#![feature(on_unimplemented)]
#![feature(rustc_attrs)]
struct Foo<T>(T);

View File

@ -1,6 +1,6 @@
// Test if the on_unimplemented message override works
#![feature(on_unimplemented)]
#![feature(rustc_attrs)]
#[rustc_on_unimplemented = "invalid"]

View File

@ -1,6 +1,6 @@
// ignore-tidy-linelength
#![feature(on_unimplemented)]
#![feature(rustc_attrs)]
pub mod Bar {
#[rustc_on_unimplemented = "test error `{Self}` with `{Bar}` `{Baz}` `{Quux}` in `{Foo}`"]