On E0277 pointing at bound in derive, suggest manual impl
When encountering a bound coming from a derive macro, suggest manual impl of the trait.
Use the span for the specific param when adding bounds in builtin derive macros, so the diagnostic will point at them as well as the derive macro itself.
```
error[E0277]: can't compare `SomeNode` with `SomeNode`
--> f29.rs:24:15
|
24 | accept_eq(&node);
| --------- ^^^^^ no implementation for `SomeNode == SomeNode`
| |
| required by a bound introduced by this call
|
= note: -Ztrack-diagnostics: created at compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs:279:39
= help: the trait `PartialEq` is not implemented for `SomeNode`
note: required for `Id<SomeNode>` to implement `PartialEq`
--> f29.rs:3:10
|
3 | #[derive(PartialEq, Eq)]
| ^^^^^^^^^ unsatisfied trait bound introduced in this `derive` macro
4 | pub struct Id<T>(PhantomData<T>);
| -
= help: consider manually implementing `PartialEq` to avoid undesired bounds
note: required by a bound in `accept_eq`
--> f29.rs:15:23
|
15 | fn accept_eq(_: &impl PartialEq) { }
| ^^^^^^^^^ required by this bound in `accept_eq`
help: consider annotating `SomeNode` with `#[derive(PartialEq)]`
|
13 + #[derive(PartialEq)]
14 | struct SomeNode();
|
```
This commit is contained in:
parent
7b62d97abd
commit
679f38c125
14 changed files with 140 additions and 26 deletions
|
|
@ -638,27 +638,27 @@ impl<'a> TraitDef<'a> {
|
|||
GenericParamKind::Type { .. } => {
|
||||
// Extra restrictions on the generics parameters to the
|
||||
// type being derived upon.
|
||||
let span = param.ident.span.with_ctxt(ctxt);
|
||||
let bounds: Vec<_> = self
|
||||
.additional_bounds
|
||||
.iter()
|
||||
.map(|p| {
|
||||
cx.trait_bound(
|
||||
p.to_path(cx, self.span, type_ident, generics),
|
||||
self.is_const,
|
||||
)
|
||||
cx.trait_bound(p.to_path(cx, span, type_ident, generics), self.is_const)
|
||||
})
|
||||
.chain(
|
||||
// Add a bound for the current trait.
|
||||
self.skip_path_as_bound
|
||||
.not()
|
||||
.then(|| cx.trait_bound(trait_path.clone(), self.is_const)),
|
||||
self.skip_path_as_bound.not().then(|| {
|
||||
let mut trait_path = trait_path.clone();
|
||||
trait_path.span = span;
|
||||
cx.trait_bound(trait_path, self.is_const)
|
||||
}),
|
||||
)
|
||||
.chain({
|
||||
// Add a `Copy` bound if required.
|
||||
if is_packed && self.needs_copy_as_bound_if_packed {
|
||||
let p = deriving::path_std!(marker::Copy);
|
||||
Some(cx.trait_bound(
|
||||
p.to_path(cx, self.span, type_ident, generics),
|
||||
p.to_path(cx, span, type_ident, generics),
|
||||
self.is_const,
|
||||
))
|
||||
} else {
|
||||
|
|
@ -671,7 +671,7 @@ impl<'a> TraitDef<'a> {
|
|||
)
|
||||
.collect();
|
||||
|
||||
cx.typaram(param.ident.span.with_ctxt(ctxt), param.ident, bounds, None)
|
||||
cx.typaram(span, param.ident, bounds, None)
|
||||
}
|
||||
GenericParamKind::Const { ty, span, .. } => {
|
||||
let const_nodefault_kind = GenericParamKind::Const {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
// ignore-tidy-filelength
|
||||
use std::borrow::Cow;
|
||||
use std::fmt;
|
||||
use std::ops::Not;
|
||||
|
||||
use rustc_abi::ExternAbi;
|
||||
use rustc_ast::attr::AttributeExt;
|
||||
|
|
@ -1012,10 +1013,14 @@ impl<'hir> Generics<'hir> {
|
|||
|
||||
span_for_parentheses.map_or_else(
|
||||
|| {
|
||||
// We include bounds that come from a `#[derive(_)]` but point at the user's code,
|
||||
// as we use this method to get a span appropriate for suggestions.
|
||||
// We include bounds that come from a `#[derive(_)]` but point at the user's
|
||||
// code, as we use this method to get a span appropriate for suggestions.
|
||||
let bs = bound.span();
|
||||
bs.can_be_used_for_suggestions().then(|| (bs.shrink_to_hi(), None))
|
||||
// We use `from_expansion` instead of `can_be_used_for_suggestions` because
|
||||
// the trait bound from imperfect derives do point at the type parameter,
|
||||
// but expanded to a where clause, so we want to ignore those. This is only
|
||||
// true for derive intrinsics.
|
||||
bs.from_expansion().not().then(|| (bs.shrink_to_hi(), None))
|
||||
},
|
||||
|span| Some((span.shrink_to_hi(), Some(span.shrink_to_lo()))),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -526,12 +526,15 @@ pub fn suggest_constraining_type_params<'a>(
|
|||
//
|
||||
// fn foo<T>(t: T) { ... }
|
||||
// - help: consider restricting this type parameter with `T: Foo`
|
||||
suggestions.push((
|
||||
param.span.shrink_to_hi(),
|
||||
post,
|
||||
format!(": {constraint}"),
|
||||
SuggestChangingConstraintsMessage::RestrictType { ty: param_name },
|
||||
));
|
||||
let span = param.span.shrink_to_hi();
|
||||
if span.can_be_used_for_suggestions() {
|
||||
suggestions.push((
|
||||
span,
|
||||
post,
|
||||
format!(": {constraint}"),
|
||||
SuggestChangingConstraintsMessage::RestrictType { ty: param_name },
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: remove the suggestions that are from derive, as the span is not correct
|
||||
|
|
|
|||
|
|
@ -3586,6 +3586,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
}
|
||||
spans.push(self_ty.span);
|
||||
let mut spans: MultiSpan = spans.into();
|
||||
let mut derived = false;
|
||||
if matches!(
|
||||
self_ty.span.ctxt().outer_expn_data().kind,
|
||||
ExpnKind::Macro(MacroKind::Derive, _)
|
||||
|
|
@ -3593,6 +3594,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
of_trait.map(|t| t.trait_ref.path.span.ctxt().outer_expn_data().kind),
|
||||
Some(ExpnKind::Macro(MacroKind::Derive, _))
|
||||
) {
|
||||
derived = true;
|
||||
spans.push_span_label(
|
||||
data.span,
|
||||
"unsatisfied trait bound introduced in this `derive` macro",
|
||||
|
|
@ -3621,6 +3623,12 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
err.span_note(spans, msg);
|
||||
if derived {
|
||||
err.help(format!(
|
||||
"consider manually implementing `{trait_name}` to avoid undesired \
|
||||
bounds",
|
||||
));
|
||||
}
|
||||
point_at_assoc_type_restriction(
|
||||
tcx,
|
||||
err,
|
||||
|
|
|
|||
|
|
@ -102,6 +102,7 @@ LL | #[derive(Debug, Copy, Clone)]
|
|||
...
|
||||
LL | Expr: Expression<SqlType=<Col::SqlType as IntoNullable>::Nullable>,
|
||||
| ------------------------------------------------ unsatisfied trait bound introduced in this `derive` macro
|
||||
= help: consider manually implementing `Debug` to avoid undesired bounds
|
||||
help: consider further restricting the associated type
|
||||
|
|
||||
LL | Expr: Expression<SqlType=<Col::SqlType as IntoNullable>::Nullable>, <Col as Expression>::SqlType: NotNull,
|
||||
|
|
@ -146,6 +147,7 @@ LL | #[derive(Debug, Copy, Clone)]
|
|||
...
|
||||
LL | Expr: Expression<SqlType=<Col::SqlType as IntoNullable>::Nullable>,
|
||||
| ------------------------------------------------ unsatisfied trait bound introduced in this `derive` macro
|
||||
= help: consider manually implementing `Copy` to avoid undesired bounds
|
||||
help: consider further restricting the associated type
|
||||
|
|
||||
LL | Expr: Expression<SqlType=<Col::SqlType as IntoNullable>::Nullable>, <Col as Expression>::SqlType: NotNull,
|
||||
|
|
@ -219,6 +221,7 @@ LL | #[derive(Debug, Copy, Clone)]
|
|||
...
|
||||
LL | Expr: Expression<SqlType=<Col::SqlType as IntoNullable>::Nullable>,
|
||||
| ------------------------------------------------ unsatisfied trait bound introduced in this `derive` macro
|
||||
= help: consider manually implementing `Clone` to avoid undesired bounds
|
||||
help: consider further restricting the associated type
|
||||
|
|
||||
LL | Expr: Expression<SqlType=<Col::SqlType as IntoNullable>::Nullable>, <Col as Expression>::SqlType: NotNull,
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@ LL | #[derive(Debug, PartialEq, Eq, ConstParamTy)]
|
|||
| ^^^^^
|
||||
LL | struct Bar<T>(T);
|
||||
| - unsatisfied trait bound introduced in this `derive` macro
|
||||
= help: consider manually implementing `Debug` to avoid undesired bounds
|
||||
= note: 2 redundant requirements hidden
|
||||
= note: required for `&&'static Bar<(dyn Debug + 'static)>` to implement `Debug`
|
||||
= note: required for the cast from `&&&'static Bar<(dyn Debug + 'static)>` to `&dyn Debug`
|
||||
|
|
@ -96,7 +97,10 @@ note: required for `Bar<dyn Debug>` to implement `Eq`
|
|||
--> $DIR/unsizing-wfcheck-issue-126272.rs:19:28
|
||||
|
|
||||
LL | #[derive(Debug, PartialEq, Eq, ConstParamTy)]
|
||||
| ^^ unsatisfied trait bound introduced in this `derive` macro
|
||||
| ^^
|
||||
LL | struct Bar<T>(T);
|
||||
| - unsatisfied trait bound introduced in this `derive` macro
|
||||
= help: consider manually implementing `Eq` to avoid undesired bounds
|
||||
= note: 1 redundant requirement hidden
|
||||
= note: required for `&'static Bar<dyn Debug>` to implement `Eq`
|
||||
note: required by a bound in `std::cmp::AssertParamIsEq`
|
||||
|
|
|
|||
|
|
@ -8,7 +8,10 @@ note: required for `Foo<String>` to implement `Copy`
|
|||
--> $DIR/trait-error.rs:1:10
|
||||
|
|
||||
LL | #[derive(Copy, Clone)]
|
||||
| ^^^^ unsatisfied trait bound introduced in this `derive` macro
|
||||
| ^^^^
|
||||
LL | struct Foo<T>(T);
|
||||
| - unsatisfied trait bound introduced in this `derive` macro
|
||||
= help: consider manually implementing `Copy` to avoid undesired bounds
|
||||
= note: the `Copy` trait is required because this value will be copied for each element of the array
|
||||
help: create an inline `const` block
|
||||
|
|
||||
|
|
|
|||
|
|
@ -10,7 +10,10 @@ note: required for `B<C>` to implement `Copy`
|
|||
--> $DIR/deriving-copyclone.rs:9:10
|
||||
|
|
||||
LL | #[derive(Copy, Clone)]
|
||||
| ^^^^ unsatisfied trait bound introduced in this `derive` macro
|
||||
| ^^^^
|
||||
LL | struct B<T> {
|
||||
| - unsatisfied trait bound introduced in this `derive` macro
|
||||
= help: consider manually implementing `Copy` to avoid undesired bounds
|
||||
note: required by a bound in `is_copy`
|
||||
--> $DIR/deriving-copyclone.rs:18:15
|
||||
|
|
||||
|
|
@ -33,7 +36,10 @@ note: required for `B<C>` to implement `Clone`
|
|||
--> $DIR/deriving-copyclone.rs:9:16
|
||||
|
|
||||
LL | #[derive(Copy, Clone)]
|
||||
| ^^^^^ unsatisfied trait bound introduced in this `derive` macro
|
||||
| ^^^^^
|
||||
LL | struct B<T> {
|
||||
| - unsatisfied trait bound introduced in this `derive` macro
|
||||
= help: consider manually implementing `Clone` to avoid undesired bounds
|
||||
note: required by a bound in `is_clone`
|
||||
--> $DIR/deriving-copyclone.rs:19:16
|
||||
|
|
||||
|
|
@ -56,7 +62,10 @@ note: required for `B<D>` to implement `Copy`
|
|||
--> $DIR/deriving-copyclone.rs:9:10
|
||||
|
|
||||
LL | #[derive(Copy, Clone)]
|
||||
| ^^^^ unsatisfied trait bound introduced in this `derive` macro
|
||||
| ^^^^
|
||||
LL | struct B<T> {
|
||||
| - unsatisfied trait bound introduced in this `derive` macro
|
||||
= help: consider manually implementing `Copy` to avoid undesired bounds
|
||||
note: required by a bound in `is_copy`
|
||||
--> $DIR/deriving-copyclone.rs:18:15
|
||||
|
|
||||
|
|
|
|||
|
|
@ -32,7 +32,10 @@ note: required for `Fooy<T>` to implement `Copy`
|
|||
--> $DIR/impl_bounds.rs:10:10
|
||||
|
|
||||
LL | #[derive(Copy, Clone)]
|
||||
| ^^^^ unsatisfied trait bound introduced in this `derive` macro
|
||||
| ^^^^
|
||||
LL | struct Fooy<T>(T);
|
||||
| - unsatisfied trait bound introduced in this `derive` macro
|
||||
= help: consider manually implementing `Copy` to avoid undesired bounds
|
||||
note: the requirement `Fooy<T>: Copy` appears on the `impl`'s associated type `C` but not on the corresponding trait's associated type
|
||||
--> $DIR/impl_bounds.rs:6:10
|
||||
|
|
||||
|
|
@ -56,7 +59,10 @@ note: required for `Fooy<T>` to implement `Copy`
|
|||
--> $DIR/impl_bounds.rs:10:10
|
||||
|
|
||||
LL | #[derive(Copy, Clone)]
|
||||
| ^^^^ unsatisfied trait bound introduced in this `derive` macro
|
||||
| ^^^^
|
||||
LL | struct Fooy<T>(T);
|
||||
| - unsatisfied trait bound introduced in this `derive` macro
|
||||
= help: consider manually implementing `Copy` to avoid undesired bounds
|
||||
note: the requirement `Fooy<T>: Copy` appears on the `impl`'s associated function `d` but not on the corresponding trait's associated function
|
||||
--> $DIR/impl_bounds.rs:7:8
|
||||
|
|
||||
|
|
|
|||
|
|
@ -37,7 +37,11 @@ note: required for `PriorityQueue<T>` to implement `PartialOrd`
|
|||
--> $DIR/issue-104884-trait-impl-sugg-err.rs:14:10
|
||||
|
|
||||
LL | #[derive(PartialOrd, AddImpl)]
|
||||
| ^^^^^^^^^^ unsatisfied trait bound introduced in this `derive` macro
|
||||
| ^^^^^^^^^^
|
||||
...
|
||||
LL | struct PriorityQueue<T>(BinaryHeap<PriorityQueueEntry<T>>);
|
||||
| - unsatisfied trait bound introduced in this `derive` macro
|
||||
= help: consider manually implementing `PartialOrd` to avoid undesired bounds
|
||||
note: required by a bound in `Ord`
|
||||
--> $SRC_DIR/core/src/cmp.rs:LL:COL
|
||||
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ LL | #[derive(Debug, Copy, Clone)]
|
|||
| ^^^^^
|
||||
LL | pub struct Vector2<T: Debug + Copy + Clone> {
|
||||
| ---- unsatisfied trait bound introduced in this `derive` macro
|
||||
= help: consider manually implementing `Debug` to avoid undesired bounds
|
||||
= note: required for the cast from `&Vector2<K>` to `&dyn Debug`
|
||||
help: consider further restricting type parameter `K` with trait `Copy`
|
||||
|
|
||||
|
|
@ -71,6 +72,7 @@ LL | #[derive(Debug, Copy, Clone)]
|
|||
| ^^^^^
|
||||
LL | pub struct Vector2<T: Debug + Copy + Clone> {
|
||||
| ---- unsatisfied trait bound introduced in this `derive` macro
|
||||
= help: consider manually implementing `Clone` to avoid undesired bounds
|
||||
help: consider further restricting type parameter `K` with trait `Copy`
|
||||
|
|
||||
LL | pub struct AABB<K: Debug + std::marker::Copy> {
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@ LL | #[derive(Debug, Copy, Clone)]
|
|||
| ^^^^^
|
||||
LL | pub struct Vector2<T: Debug + Copy + Clone> {
|
||||
| ---- unsatisfied trait bound introduced in this `derive` macro
|
||||
= help: consider manually implementing `Debug` to avoid undesired bounds
|
||||
= note: required for the cast from `&Vector2<K>` to `&dyn Debug`
|
||||
help: consider restricting type parameter `K` with trait `Copy`
|
||||
|
|
||||
|
|
@ -134,6 +135,7 @@ LL | #[derive(Debug, Copy, Clone)]
|
|||
| ^^^^^
|
||||
LL | pub struct Vector2<T: Debug + Copy + Clone> {
|
||||
| ---- unsatisfied trait bound introduced in this `derive` macro
|
||||
= help: consider manually implementing `Clone` to avoid undesired bounds
|
||||
help: consider restricting type parameter `K` with trait `Copy`
|
||||
|
|
||||
LL | pub struct AABB<K: std::marker::Copy> {
|
||||
|
|
|
|||
34
tests/ui/traits/derive-implicit-bound.rs
Normal file
34
tests/ui/traits/derive-implicit-bound.rs
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
// Second case reported in issue #108894.
|
||||
|
||||
use std::marker::PhantomData;
|
||||
|
||||
#[derive(PartialEq, Eq)]
|
||||
pub struct Id<T>(PhantomData<T>);
|
||||
|
||||
// manual implementation which would break the usage of const patterns
|
||||
// impl<T> PartialEq for Id<T> { fn eq(&self, _: &Id<T>) -> bool { true } }
|
||||
// impl<T> Eq for Id<T> {}
|
||||
|
||||
// This derive is undesired but cannot be removed without
|
||||
// breaking the usages below
|
||||
// #[derive(PartialEq, Eq)]
|
||||
struct SomeNode();
|
||||
|
||||
fn accept_eq(_: &impl PartialEq) { }
|
||||
|
||||
fn main() {
|
||||
let node = Id::<SomeNode>(PhantomData);
|
||||
|
||||
// this will only work if
|
||||
// - `Partial/Eq` is implemented manually, or
|
||||
// - `SomeNode` also needlessly(?) implements `Partial/Eq`
|
||||
accept_eq(&node); //~ ERROR can't compare `SomeNode` with `SomeNode`
|
||||
|
||||
const CONST_ID: Id::<SomeNode> = Id::<SomeNode>(PhantomData);
|
||||
// this will work only when `Partial/Eq` is being derived
|
||||
// otherwise: error: to use a constant of type `Id<SomeNode>` in a pattern,
|
||||
// `Id<SomeNode>` must be annotated with `#[derive(PartialEq, Eq)]`
|
||||
match node {
|
||||
CONST_ID => {}
|
||||
}
|
||||
}
|
||||
31
tests/ui/traits/derive-implicit-bound.stderr
Normal file
31
tests/ui/traits/derive-implicit-bound.stderr
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
error[E0277]: can't compare `SomeNode` with `SomeNode`
|
||||
--> $DIR/derive-implicit-bound.rs:25:15
|
||||
|
|
||||
LL | accept_eq(&node);
|
||||
| --------- ^^^^^ no implementation for `SomeNode == SomeNode`
|
||||
| |
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
= help: the trait `PartialEq` is not implemented for `SomeNode`
|
||||
note: required for `Id<SomeNode>` to implement `PartialEq`
|
||||
--> $DIR/derive-implicit-bound.rs:5:10
|
||||
|
|
||||
LL | #[derive(PartialEq, Eq)]
|
||||
| ^^^^^^^^^
|
||||
LL | pub struct Id<T>(PhantomData<T>);
|
||||
| - unsatisfied trait bound introduced in this `derive` macro
|
||||
= help: consider manually implementing `PartialEq` to avoid undesired bounds
|
||||
note: required by a bound in `accept_eq`
|
||||
--> $DIR/derive-implicit-bound.rs:17:23
|
||||
|
|
||||
LL | fn accept_eq(_: &impl PartialEq) { }
|
||||
| ^^^^^^^^^ required by this bound in `accept_eq`
|
||||
help: consider annotating `SomeNode` with `#[derive(PartialEq)]`
|
||||
|
|
||||
LL + #[derive(PartialEq)]
|
||||
LL | struct SomeNode();
|
||||
|
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0277`.
|
||||
Loading…
Add table
Add a link
Reference in a new issue