diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 19e2679ae4da7..2c2fffe34c615 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -2126,6 +2126,12 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { &mut vec![], &mut Default::default(), ); + self.suggest_swapping_lhs_and_rhs( + err, + obligation.predicate, + obligation.param_env, + obligation.cause.code(), + ); self.suggest_unsized_bound_if_applicable(err, obligation); if let Some(span) = err.span.primary_span() && let Some(mut diag) = 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 6df7fac949c2f..7deaebe61256d 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -4843,6 +4843,53 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ); true } + pub(crate) fn suggest_swapping_lhs_and_rhs( + &self, + err: &mut Diag<'_>, + predicate: T, + param_env: ty::ParamEnv<'tcx>, + cause_code: &ObligationCauseCode<'tcx>, + ) where + T: Upcast, ty::Predicate<'tcx>>, + { + let tcx = self.tcx; + let predicate = predicate.upcast(tcx); + match *cause_code { + ObligationCauseCode::BinOp { + lhs_hir_id, + rhs_hir_id: Some(rhs_hir_id), + rhs_span: Some(rhs_span), + .. + } if let Some(typeck_results) = &self.typeck_results + && let hir::Node::Expr(lhs) = tcx.hir_node(lhs_hir_id) + && let hir::Node::Expr(rhs) = tcx.hir_node(rhs_hir_id) + && let Some(lhs_ty) = typeck_results.expr_ty_opt(lhs) + && let Some(rhs_ty) = typeck_results.expr_ty_opt(rhs) => + { + if let Some(pred) = predicate.as_trait_clause() + && tcx.is_lang_item(pred.def_id(), LangItem::PartialEq) + && self + .infcx + .type_implements_trait(pred.def_id(), [rhs_ty, lhs_ty], param_env) + .must_apply_modulo_regions() + { + let lhs_span = tcx.hir().span(lhs_hir_id); + let sm = tcx.sess.source_map(); + if let Ok(rhs_snippet) = sm.span_to_snippet(rhs_span) + && let Ok(lhs_snippet) = sm.span_to_snippet(lhs_span) + { + err.note(format!("`{rhs_ty}` implements `PartialEq<{lhs_ty}>`")); + err.multipart_suggestion( + "consider swapping the equality", + vec![(lhs_span, rhs_snippet), (rhs_span, lhs_snippet)], + Applicability::MaybeIncorrect, + ); + } + } + } + _ => {} + } + } } /// Add a hint to add a missing borrow or remove an unnecessary one. diff --git a/tests/ui/suggestions/partialeq_suggest_swap_on_e0277.rs b/tests/ui/suggestions/partialeq_suggest_swap_on_e0277.rs new file mode 100644 index 0000000000000..2ebbed3c740f4 --- /dev/null +++ b/tests/ui/suggestions/partialeq_suggest_swap_on_e0277.rs @@ -0,0 +1,11 @@ +struct T(String); + +impl PartialEq for T { + fn eq(&self, other: &String) -> bool { + &self.0 == other + } +} + +fn main() { + String::from("Girls Band Cry") == T(String::from("Girls Band Cry")); //~ can't compare `String` with `T` [E0277] +} diff --git a/tests/ui/suggestions/partialeq_suggest_swap_on_e0277.stderr b/tests/ui/suggestions/partialeq_suggest_swap_on_e0277.stderr new file mode 100644 index 0000000000000..ebe103ef19a1c --- /dev/null +++ b/tests/ui/suggestions/partialeq_suggest_swap_on_e0277.stderr @@ -0,0 +1,24 @@ +error[E0277]: can't compare `String` with `T` + --> $DIR/partialeq_suggest_swap_on_e0277.rs:10:36 + | +LL | String::from("Girls Band Cry") == T(String::from("Girls Band Cry")); + | ^^ no implementation for `String == T` + | + = help: the trait `PartialEq` is not implemented for `String` + = help: the following other types implement trait `PartialEq`: + `String` implements `PartialEq<&str>` + `String` implements `PartialEq` + `String` implements `PartialEq` + `String` implements `PartialEq>` + `String` implements `PartialEq` + `String` implements `PartialEq` + = note: `T` implements `PartialEq` +help: consider swapping the equality + | +LL - String::from("Girls Band Cry") == T(String::from("Girls Band Cry")); +LL + T(String::from("Girls Band Cry")) == String::from("Girls Band Cry"); + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`.