From b0c41eb415c3dbb696a872be7afa159eac33ab82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sat, 20 Jul 2024 01:45:39 +0000 Subject: [PATCH 1/3] Suggest `derive(Trait)` or `T: Trait` from transitive obligation in some cases With code like the following ```rust struct Ctx { a_map: HashMap>, } struct B { a: A, } ``` the derived trait will have an implicit restriction on `A: Clone` for both types. When referenced as follows: ```rust fn foo(ctx: &mut Ctx) { let a_map = ctx.a_map.clone(); //~ ERROR E0599 } ``` suggest constraining `Z`: ``` error[E0599]: the method `clone` exists for struct `HashMap>`, but its trait bounds were not satisfied --> $DIR/type-or-type-param-missing-transitive-trait-contraint.rs:16:27 | LL | struct B { | ----------- doesn't satisfy `B: Clone` ... LL | let a_map = ctx.a_map.clone(); | ^^^^^ method cannot be called on `HashMap>` due to unsatisfied trait bounds | = note: the following trait bounds were not satisfied: `B: Clone` which is required by `HashMap>: Clone` help: consider restricting type parameter `Z` | LL | fn foo(ctx: &mut Ctx) { | +++++++++++++++++++ ``` When referenced as follows, with a specific type `S`: ```rust struct S; fn bar(ctx: &mut Ctx) { let a_map = ctx.a_map.clone(); //~ ERROR E0599 } ``` suggest `derive`ing the appropriate trait on the local type: ``` error[E0599]: the method `clone` exists for struct `HashMap>`, but its trait bounds were not satisfied --> $DIR/type-or-type-param-missing-transitive-trait-contraint.rs:21:27 | LL | struct B { | ----------- doesn't satisfy `B: Clone` ... LL | let a_map = ctx.a_map.clone(); | ^^^^^ method cannot be called on `HashMap>` due to unsatisfied trait bounds | = note: the following trait bounds were not satisfied: `B: Clone` which is required by `HashMap>: Clone` help: consider annotating `S` with `#[derive(Clone)]` | LL + #[derive(Clone)] LL | struct S; | ``` --- .../rustc_hir_typeck/src/method/suggest.rs | 100 +++++++++++++++++- .../src/error_reporting/traits/suggestions.rs | 20 ++-- ...m-missing-transitive-trait-contraint.fixed | 25 +++++ ...aram-missing-transitive-trait-contraint.rs | 24 +++++ ...-missing-transitive-trait-contraint.stderr | 38 +++++++ 5 files changed, 195 insertions(+), 12 deletions(-) create mode 100644 tests/ui/suggestions/type-or-type-param-missing-transitive-trait-contraint.fixed create mode 100644 tests/ui/suggestions/type-or-type-param-missing-transitive-trait-contraint.rs create mode 100644 tests/ui/suggestions/type-or-type-param-missing-transitive-trait-contraint.stderr diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 94133357a3eb5..186f212d368c3 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -31,7 +31,10 @@ use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::{ edit_distance, ErrorGuaranteed, ExpnKind, FileName, MacroKind, Span, Symbol, DUMMY_SP, }; -use rustc_trait_selection::error_reporting::traits::on_unimplemented::OnUnimplementedNote; +use rustc_trait_selection::error_reporting::traits::on_unimplemented::{ + OnUnimplementedNote, TypeErrCtxtExt as _, +}; +use rustc_trait_selection::error_reporting::traits::suggestions::TypeErrCtxtExt; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; use rustc_trait_selection::traits::{ @@ -1363,7 +1366,100 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "the following trait bounds were not satisfied:\n{bound_list}" )); } - suggested_derive = self.suggest_derive(&mut err, unsatisfied_predicates); + + let mut suggest_derive = true; + for (pred, _, cause) in unsatisfied_predicates { + let Some(ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_pred))) = + pred.kind().no_bound_vars() + else { + continue; + }; + let (adt, params) = match trait_pred.self_ty().kind() { + ty::Adt(adt, params) if adt.did().is_local() => (*adt, params), + _ => continue, + }; + if self + .tcx + .all_impls(trait_pred.def_id()) + .filter_map(|imp_did| { + self.tcx.impl_trait_header(imp_did).map(|h| (imp_did, h)) + }) + .filter(|(did, header)| { + let imp = header.trait_ref.instantiate_identity(); + let impl_adt = match imp.self_ty().ty_adt_def() { + Some(impl_adt) if adt.did().is_local() => impl_adt, + _ => return false, + }; + header.polarity == ty::ImplPolarity::Positive + && impl_adt == adt + && self.tcx.is_automatically_derived(*did) + }) + .count() + == 1 + { + // We now know that for this predicate, there *was* a `derive(Trait)` for + // the trait at hand, so we don't want to suggest writing that again. + for param in ¶ms[..] { + // Look at the type parameters for the currently obligated type to see + // if a restriciton of `TypeParam: Trait` would help. If the + // instantiated type param is not a type param but instead an actual + // type, see if we can suggest `derive(Trait)` on *that* type. + // See `tests/ui/suggestions/f1000.rs` + let Some(ty) = param.as_type() else { + continue; + }; + match ty.kind() { + ty::Adt(adt, _) if adt.did().is_local() => { + // The type param at hand is a local type, try to suggest + // `derive(Trait)`. + let trait_ref = + ty::TraitRef::new(tcx, trait_pred.trait_ref.def_id, [ty]); + let trait_pred = ty::Binder::dummy(ty::TraitPredicate { + trait_ref, + polarity: ty::PredicatePolarity::Positive, + }); + suggested_derive = self.suggest_derive( + &mut err, + &[( + <_ as ty::UpcastFrom<_, _>>::upcast_from( + trait_pred, self.tcx, + ), + None, + cause.clone(), + )], + ); + } + ty::Param(_) => { + // It was a type param. See if it corresponds to the current + // `fn` and suggest `T: Trait`. + if let Some(obligation) = cause { + let trait_ref = ty::TraitRef::new( + tcx, + trait_pred.trait_ref.def_id, + [ty], + ); + let trait_pred = ty::Binder::dummy(ty::TraitPredicate { + trait_ref, + polarity: ty::PredicatePolarity::Positive, + }); + suggested_derive = + self.err_ctxt().suggest_restricting_param_bound( + &mut err, + trait_pred, + None, + obligation.body_id, + ); + } + } + _ => {} + } + } + suggest_derive = false + } + } + if suggest_derive { + suggested_derive = self.suggest_derive(&mut err, unsatisfied_predicates); + } unsatisfied_bounds = true; } diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index 0d15ef55e24e7..150336badef6a 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -242,9 +242,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { trait_pred: ty::PolyTraitPredicate<'tcx>, associated_ty: Option<(&'static str, Ty<'tcx>)>, mut body_id: LocalDefId, - ) { + ) -> bool { if trait_pred.skip_binder().polarity != ty::PredicatePolarity::Positive { - return; + return false; } let trait_pred = self.resolve_numeric_literals_with_default(trait_pred); @@ -279,7 +279,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { trait_pred, Some((ident, bounds)), ); - return; + return true; } hir::Node::TraitItem(hir::TraitItem { @@ -293,7 +293,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { self.tcx, body_id, generics, "`Self`", err, None, projection, trait_pred, None, ); - return; + return true; } hir::Node::TraitItem(hir::TraitItem { @@ -321,7 +321,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { trait_pred, None, ); - return; + return true; } hir::Node::Item(hir::Item { kind: @@ -341,7 +341,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { trait_pred, None, ); - return; + return true; } hir::Node::Item(hir::Item { @@ -374,7 +374,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { .iter() .all(|g| g.is_suggestable(self.tcx, false)) { - return; + return false; } // Missing generic type parameter bound. let param_name = self_ty.to_string(); @@ -406,7 +406,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { Some(trait_pred.def_id()), None, ) { - return; + return true; } } @@ -432,10 +432,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { trait_pred, associated_ty, ) { - return; + return true; } } - hir::Node::Crate(..) => return, + hir::Node::Crate(..) => return false, _ => {} } diff --git a/tests/ui/suggestions/type-or-type-param-missing-transitive-trait-contraint.fixed b/tests/ui/suggestions/type-or-type-param-missing-transitive-trait-contraint.fixed new file mode 100644 index 0000000000000..297f6ce7bb651 --- /dev/null +++ b/tests/ui/suggestions/type-or-type-param-missing-transitive-trait-contraint.fixed @@ -0,0 +1,25 @@ +//@ run-rustfix +#![allow(warnings)] +use std::collections::HashMap; + +#[derive(Clone)] +struct Ctx { + a_map: HashMap>, +} + +#[derive(Clone)] +struct B { + a: A, +} + +fn foo(ctx: &mut Ctx) { + let a_map = ctx.a_map.clone(); //~ ERROR E0599 +} + +#[derive(Clone)] +struct S; +fn bar(ctx: &mut Ctx) { + let a_map = ctx.a_map.clone(); //~ ERROR E0599 +} + +fn main() {} diff --git a/tests/ui/suggestions/type-or-type-param-missing-transitive-trait-contraint.rs b/tests/ui/suggestions/type-or-type-param-missing-transitive-trait-contraint.rs new file mode 100644 index 0000000000000..640c9a66158da --- /dev/null +++ b/tests/ui/suggestions/type-or-type-param-missing-transitive-trait-contraint.rs @@ -0,0 +1,24 @@ +//@ run-rustfix +#![allow(warnings)] +use std::collections::HashMap; + +#[derive(Clone)] +struct Ctx { + a_map: HashMap>, +} + +#[derive(Clone)] +struct B { + a: A, +} + +fn foo(ctx: &mut Ctx) { + let a_map = ctx.a_map.clone(); //~ ERROR E0599 +} + +struct S; +fn bar(ctx: &mut Ctx) { + let a_map = ctx.a_map.clone(); //~ ERROR E0599 +} + +fn main() {} diff --git a/tests/ui/suggestions/type-or-type-param-missing-transitive-trait-contraint.stderr b/tests/ui/suggestions/type-or-type-param-missing-transitive-trait-contraint.stderr new file mode 100644 index 0000000000000..b18a7fffd3be9 --- /dev/null +++ b/tests/ui/suggestions/type-or-type-param-missing-transitive-trait-contraint.stderr @@ -0,0 +1,38 @@ +error[E0599]: the method `clone` exists for struct `HashMap>`, but its trait bounds were not satisfied + --> $DIR/type-or-type-param-missing-transitive-trait-contraint.rs:16:27 + | +LL | struct B { + | ----------- doesn't satisfy `B: Clone` +... +LL | let a_map = ctx.a_map.clone(); + | ^^^^^ method cannot be called on `HashMap>` due to unsatisfied trait bounds + | + = note: the following trait bounds were not satisfied: + `B: Clone` + which is required by `HashMap>: Clone` +help: consider restricting type parameter `Z` + | +LL | fn foo(ctx: &mut Ctx) { + | +++++++++++++++++++ + +error[E0599]: the method `clone` exists for struct `HashMap>`, but its trait bounds were not satisfied + --> $DIR/type-or-type-param-missing-transitive-trait-contraint.rs:21:27 + | +LL | struct B { + | ----------- doesn't satisfy `B: Clone` +... +LL | let a_map = ctx.a_map.clone(); + | ^^^^^ method cannot be called on `HashMap>` due to unsatisfied trait bounds + | + = note: the following trait bounds were not satisfied: + `B: Clone` + which is required by `HashMap>: Clone` +help: consider annotating `S` with `#[derive(Clone)]` + | +LL + #[derive(Clone)] +LL | struct S; + | + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0599`. From 7bf75ab17dffb608d96a310d41ef10e5883d0e01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Thu, 8 Aug 2024 19:09:32 +0000 Subject: [PATCH 2/3] Restrict suggestion only to built-in `derive` traits Address review comments and rebase. Add more tests. --- .../rustc_hir_typeck/src/method/suggest.rs | 31 +++++++---- ...m-missing-transitive-trait-contraint.fixed | 11 +++- ...aram-missing-transitive-trait-contraint.rs | 10 +++- ...-missing-transitive-trait-contraint.stderr | 54 ++++++++++++++++++- 4 files changed, 92 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 186f212d368c3..032a841798811 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -31,10 +31,7 @@ use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::{ edit_distance, ErrorGuaranteed, ExpnKind, FileName, MacroKind, Span, Symbol, DUMMY_SP, }; -use rustc_trait_selection::error_reporting::traits::on_unimplemented::{ - OnUnimplementedNote, TypeErrCtxtExt as _, -}; -use rustc_trait_selection::error_reporting::traits::suggestions::TypeErrCtxtExt; +use rustc_trait_selection::error_reporting::traits::on_unimplemented::OnUnimplementedNote; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; use rustc_trait_selection::traits::{ @@ -1374,16 +1371,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { else { continue; }; + let lang = tcx.lang_items(); + if ![ + lang.copy_trait(), + lang.clone_trait(), + tcx.get_diagnostic_item(sym::Debug), + tcx.get_diagnostic_item(sym::Eq), + tcx.get_diagnostic_item(sym::PartialEq), + tcx.get_diagnostic_item(sym::Default), + ] + .contains(&Some(trait_pred.def_id())) + { + // We restrict ourselves only to built-in `derive`s. + continue; + } let (adt, params) = match trait_pred.self_ty().kind() { ty::Adt(adt, params) if adt.did().is_local() => (*adt, params), _ => continue, }; - if self - .tcx + if tcx .all_impls(trait_pred.def_id()) - .filter_map(|imp_did| { - self.tcx.impl_trait_header(imp_did).map(|h| (imp_did, h)) - }) + .filter_map(|imp_did| tcx.impl_trait_header(imp_did).map(|h| (imp_did, h))) .filter(|(did, header)| { let imp = header.trait_ref.instantiate_identity(); let impl_adt = match imp.self_ty().ty_adt_def() { @@ -1392,7 +1400,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; header.polarity == ty::ImplPolarity::Positive && impl_adt == adt - && self.tcx.is_automatically_derived(*did) + && tcx.is_automatically_derived(*did) }) .count() == 1 @@ -1413,7 +1421,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // The type param at hand is a local type, try to suggest // `derive(Trait)`. let trait_ref = - ty::TraitRef::new(tcx, trait_pred.trait_ref.def_id, [ty]); + ty::TraitRef::identity(tcx, trait_pred.trait_ref.def_id) + .with_self_ty(tcx, ty); let trait_pred = ty::Binder::dummy(ty::TraitPredicate { trait_ref, polarity: ty::PredicatePolarity::Positive, diff --git a/tests/ui/suggestions/type-or-type-param-missing-transitive-trait-contraint.fixed b/tests/ui/suggestions/type-or-type-param-missing-transitive-trait-contraint.fixed index 297f6ce7bb651..62e0081785fe8 100644 --- a/tests/ui/suggestions/type-or-type-param-missing-transitive-trait-contraint.fixed +++ b/tests/ui/suggestions/type-or-type-param-missing-transitive-trait-contraint.fixed @@ -7,7 +7,7 @@ struct Ctx { a_map: HashMap>, } -#[derive(Clone)] +#[derive(Clone, PartialEq, Eq)] struct B { a: A, } @@ -17,9 +17,18 @@ fn foo(ctx: &mut Ctx) { } #[derive(Clone)] +#[derive(PartialEq)] struct S; fn bar(ctx: &mut Ctx) { let a_map = ctx.a_map.clone(); //~ ERROR E0599 } +fn qux(ctx: &mut Ctx) where Z: PartialEq { + ctx.a_map["a"].eq(&ctx.a_map["a"]); //~ ERROR E0599 +} + +fn qut(ctx: &mut Ctx) { + ctx.a_map["a"].eq(&ctx.a_map["a"]); //~ ERROR E0599 +} + fn main() {} diff --git a/tests/ui/suggestions/type-or-type-param-missing-transitive-trait-contraint.rs b/tests/ui/suggestions/type-or-type-param-missing-transitive-trait-contraint.rs index 640c9a66158da..00f8f687d106f 100644 --- a/tests/ui/suggestions/type-or-type-param-missing-transitive-trait-contraint.rs +++ b/tests/ui/suggestions/type-or-type-param-missing-transitive-trait-contraint.rs @@ -7,7 +7,7 @@ struct Ctx { a_map: HashMap>, } -#[derive(Clone)] +#[derive(Clone, PartialEq, Eq)] struct B { a: A, } @@ -21,4 +21,12 @@ fn bar(ctx: &mut Ctx) { let a_map = ctx.a_map.clone(); //~ ERROR E0599 } +fn qux(ctx: &mut Ctx) { + ctx.a_map["a"].eq(&ctx.a_map["a"]); //~ ERROR E0599 +} + +fn qut(ctx: &mut Ctx) { + ctx.a_map["a"].eq(&ctx.a_map["a"]); //~ ERROR E0599 +} + fn main() {} diff --git a/tests/ui/suggestions/type-or-type-param-missing-transitive-trait-contraint.stderr b/tests/ui/suggestions/type-or-type-param-missing-transitive-trait-contraint.stderr index b18a7fffd3be9..b19c516ba8476 100644 --- a/tests/ui/suggestions/type-or-type-param-missing-transitive-trait-contraint.stderr +++ b/tests/ui/suggestions/type-or-type-param-missing-transitive-trait-contraint.stderr @@ -33,6 +33,58 @@ LL + #[derive(Clone)] LL | struct S; | -error: aborting due to 2 previous errors +error[E0599]: the method `eq` exists for struct `B`, but its trait bounds were not satisfied + --> $DIR/type-or-type-param-missing-transitive-trait-contraint.rs:25:20 + | +LL | struct B { + | ----------- method `eq` not found for this struct because it doesn't satisfy `B: Iterator` or `B: PartialEq` +... +LL | ctx.a_map["a"].eq(&ctx.a_map["a"]); + | ^^ method cannot be called on `B` due to unsatisfied trait bounds + | +note: trait bound `Z: PartialEq` was not satisfied + --> $DIR/type-or-type-param-missing-transitive-trait-contraint.rs:10:17 + | +LL | #[derive(Clone, PartialEq, Eq)] + | ^^^^^^^^^ unsatisfied trait bound introduced in this `derive` macro + = note: the following trait bounds were not satisfied: + `B: Iterator` + which is required by `&mut B: Iterator` +note: the trait `Iterator` must be implemented + --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL +help: consider restricting the type parameter to satisfy the trait bound + | +LL | fn qux(ctx: &mut Ctx) where Z: PartialEq { + | ++++++++++++++++++ + +error[E0599]: the method `eq` exists for struct `B`, but its trait bounds were not satisfied + --> $DIR/type-or-type-param-missing-transitive-trait-contraint.rs:29:20 + | +LL | struct B { + | ----------- method `eq` not found for this struct because it doesn't satisfy `B: Iterator` or `B: PartialEq` +... +LL | struct S; + | -------- doesn't satisfy `S: PartialEq` +... +LL | ctx.a_map["a"].eq(&ctx.a_map["a"]); + | ^^ method cannot be called on `B` due to unsatisfied trait bounds + | +note: trait bound `S: PartialEq` was not satisfied + --> $DIR/type-or-type-param-missing-transitive-trait-contraint.rs:10:17 + | +LL | #[derive(Clone, PartialEq, Eq)] + | ^^^^^^^^^ unsatisfied trait bound introduced in this `derive` macro + = note: the following trait bounds were not satisfied: + `B: Iterator` + which is required by `&mut B: Iterator` +note: the trait `Iterator` must be implemented + --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL +help: consider annotating `S` with `#[derive(PartialEq)]` + | +LL + #[derive(PartialEq)] +LL | struct S; + | + +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0599`. From c3e1297bd18d927018de741bc55162a3b9251f96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Thu, 8 Aug 2024 19:19:00 +0000 Subject: [PATCH 3/3] Do not suggest `Eq`, we already suggest it in E0277 --- .../rustc_hir_typeck/src/method/suggest.rs | 1 - ...m-missing-transitive-trait-contraint.fixed | 5 ++- ...aram-missing-transitive-trait-contraint.rs | 2 + ...-missing-transitive-trait-contraint.stderr | 43 +++++++++++++++++-- 4 files changed, 46 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 032a841798811..427447e481525 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -1376,7 +1376,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { lang.copy_trait(), lang.clone_trait(), tcx.get_diagnostic_item(sym::Debug), - tcx.get_diagnostic_item(sym::Eq), tcx.get_diagnostic_item(sym::PartialEq), tcx.get_diagnostic_item(sym::Default), ] diff --git a/tests/ui/suggestions/type-or-type-param-missing-transitive-trait-contraint.fixed b/tests/ui/suggestions/type-or-type-param-missing-transitive-trait-contraint.fixed index 62e0081785fe8..00339a1de6d08 100644 --- a/tests/ui/suggestions/type-or-type-param-missing-transitive-trait-contraint.fixed +++ b/tests/ui/suggestions/type-or-type-param-missing-transitive-trait-contraint.fixed @@ -18,17 +18,20 @@ fn foo(ctx: &mut Ctx) { #[derive(Clone)] #[derive(PartialEq)] +#[derive(Eq)] struct S; fn bar(ctx: &mut Ctx) { let a_map = ctx.a_map.clone(); //~ ERROR E0599 } -fn qux(ctx: &mut Ctx) where Z: PartialEq { +fn qux(ctx: &mut Ctx) where Z: PartialEq { ctx.a_map["a"].eq(&ctx.a_map["a"]); //~ ERROR E0599 + <_ as Eq>::assert_receiver_is_total_eq(&ctx.a_map["a"]); //~ ERROR E0277 } fn qut(ctx: &mut Ctx) { ctx.a_map["a"].eq(&ctx.a_map["a"]); //~ ERROR E0599 + <_ as Eq>::assert_receiver_is_total_eq(&ctx.a_map["a"]); //~ ERROR E0277 } fn main() {} diff --git a/tests/ui/suggestions/type-or-type-param-missing-transitive-trait-contraint.rs b/tests/ui/suggestions/type-or-type-param-missing-transitive-trait-contraint.rs index 00f8f687d106f..99b67145f1596 100644 --- a/tests/ui/suggestions/type-or-type-param-missing-transitive-trait-contraint.rs +++ b/tests/ui/suggestions/type-or-type-param-missing-transitive-trait-contraint.rs @@ -23,10 +23,12 @@ fn bar(ctx: &mut Ctx) { fn qux(ctx: &mut Ctx) { ctx.a_map["a"].eq(&ctx.a_map["a"]); //~ ERROR E0599 + <_ as Eq>::assert_receiver_is_total_eq(&ctx.a_map["a"]); //~ ERROR E0277 } fn qut(ctx: &mut Ctx) { ctx.a_map["a"].eq(&ctx.a_map["a"]); //~ ERROR E0599 + <_ as Eq>::assert_receiver_is_total_eq(&ctx.a_map["a"]); //~ ERROR E0277 } fn main() {} diff --git a/tests/ui/suggestions/type-or-type-param-missing-transitive-trait-contraint.stderr b/tests/ui/suggestions/type-or-type-param-missing-transitive-trait-contraint.stderr index b19c516ba8476..41fcd807c5269 100644 --- a/tests/ui/suggestions/type-or-type-param-missing-transitive-trait-contraint.stderr +++ b/tests/ui/suggestions/type-or-type-param-missing-transitive-trait-contraint.stderr @@ -57,8 +57,25 @@ help: consider restricting the type parameter to satisfy the trait bound LL | fn qux(ctx: &mut Ctx) where Z: PartialEq { | ++++++++++++++++++ +error[E0277]: the trait bound `Z: Eq` is not satisfied + --> $DIR/type-or-type-param-missing-transitive-trait-contraint.rs:26:6 + | +LL | <_ as Eq>::assert_receiver_is_total_eq(&ctx.a_map["a"]); + | ^ the trait `Eq` is not implemented for `Z`, which is required by `B: Eq` + | +note: required for `B` to implement `Eq` + --> $DIR/type-or-type-param-missing-transitive-trait-contraint.rs:10:28 + | +LL | #[derive(Clone, PartialEq, Eq)] + | ^^ unsatisfied trait bound introduced in this `derive` macro + = note: this error originates in the derive macro `Eq` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider restricting type parameter `Z` + | +LL | fn qux(ctx: &mut Ctx) { + | ++++++++++++++ + error[E0599]: the method `eq` exists for struct `B`, but its trait bounds were not satisfied - --> $DIR/type-or-type-param-missing-transitive-trait-contraint.rs:29:20 + --> $DIR/type-or-type-param-missing-transitive-trait-contraint.rs:30:20 | LL | struct B { | ----------- method `eq` not found for this struct because it doesn't satisfy `B: Iterator` or `B: PartialEq` @@ -85,6 +102,26 @@ LL + #[derive(PartialEq)] LL | struct S; | -error: aborting due to 4 previous errors +error[E0277]: the trait bound `S: Eq` is not satisfied + --> $DIR/type-or-type-param-missing-transitive-trait-contraint.rs:31:6 + | +LL | <_ as Eq>::assert_receiver_is_total_eq(&ctx.a_map["a"]); + | ^ the trait `Eq` is not implemented for `S`, which is required by `B: Eq` + | + = help: the trait `Eq` is implemented for `B` +note: required for `B` to implement `Eq` + --> $DIR/type-or-type-param-missing-transitive-trait-contraint.rs:10:28 + | +LL | #[derive(Clone, PartialEq, Eq)] + | ^^ unsatisfied trait bound introduced in this `derive` macro + = note: this error originates in the derive macro `Eq` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider annotating `S` with `#[derive(Eq)]` + | +LL + #[derive(Eq)] +LL | struct S; + | + +error: aborting due to 6 previous errors -For more information about this error, try `rustc --explain E0599`. +Some errors have detailed explanations: E0277, E0599. +For more information about an error, try `rustc --explain E0277`.