From 2618e0f805bda52ef07704872d9751ee22e40eda Mon Sep 17 00:00:00 2001 From: sjwang05 <63834813+sjwang05@users.noreply.github.com> Date: Sat, 16 Dec 2023 17:34:52 -0800 Subject: [PATCH] Provide better suggestions for T == &T and &T == T --- Cargo.lock | 1 + .../src/fn_ctxt/suggestions.rs | 2 +- compiler/rustc_hir_typeck/src/op.rs | 23 +- compiler/rustc_middle/src/traits/mod.rs | 19 +- compiler/rustc_trait_selection/Cargo.toml | 1 + .../src/traits/error_reporting/suggestions.rs | 365 ++++++++++++------ tests/ui/binop/binary-op-suggest-deref.fixed | 8 - tests/ui/binop/binary-op-suggest-deref.rs | 73 +++- tests/ui/binop/binary-op-suggest-deref.stderr | 296 +++++++++++++- tests/ui/dst/issue-113447.fixed | 25 -- tests/ui/dst/issue-113447.rs | 2 - tests/ui/dst/issue-113447.stderr | 19 +- tests/ui/partialeq_help.stderr | 8 + 13 files changed, 668 insertions(+), 174 deletions(-) delete mode 100644 tests/ui/binop/binary-op-suggest-deref.fixed delete mode 100644 tests/ui/dst/issue-113447.fixed diff --git a/Cargo.lock b/Cargo.lock index a5bdc14fe998c..9e28c1158a339 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4580,6 +4580,7 @@ checksum = "8ba09476327c4b70ccefb6180f046ef588c26a24cf5d269a9feba316eb4f029f" name = "rustc_trait_selection" version = "0.0.0" dependencies = [ + "itertools", "rustc_ast", "rustc_attr", "rustc_data_structures", diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 28f377083f68f..64736bb1cae0b 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -2441,7 +2441,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } }; - // Suggest dereferencing the lhs for expressions such as `&T == T` + // Suggest dereferencing the lhs for expressions such as `&T <= T` if let Some(hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Binary(_, lhs, ..), .. diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs index 59b4b0032f80d..68156ca563c74 100644 --- a/compiler/rustc_hir_typeck/src/op.rs +++ b/compiler/rustc_hir_typeck/src/op.rs @@ -47,7 +47,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Some(lhs_deref_ty) = self.deref_once_mutably_for_diagnostic(lhs_ty) { if self .lookup_op_method( - lhs_deref_ty, + (lhs, lhs_deref_ty), Some((rhs, rhs_ty)), Op::Binary(op, IsAssign::Yes), expected, @@ -58,7 +58,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // emitted a better suggestion during error handling in check_overloaded_binop. if self .lookup_op_method( - lhs_ty, + (lhs, lhs_ty), Some((rhs, rhs_ty)), Op::Binary(op, IsAssign::Yes), expected, @@ -246,7 +246,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }); let result = self.lookup_op_method( - lhs_ty, + (lhs_expr, lhs_ty), Some((rhs_expr, rhs_ty_var)), Op::Binary(op, is_assign), expected, @@ -391,7 +391,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { |err: &mut DiagnosticBuilder<'_, _>, lhs_deref_ty: Ty<'tcx>| { if self .lookup_op_method( - lhs_deref_ty, + (lhs_expr, lhs_deref_ty), Some((rhs_expr, rhs_ty)), Op::Binary(op, is_assign), expected, @@ -424,7 +424,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { rhs_new_mutbl: Option| { if self .lookup_op_method( - lhs_adjusted_ty, + (lhs_expr, lhs_adjusted_ty), Some((rhs_expr, rhs_adjusted_ty)), Op::Binary(op, is_assign), expected, @@ -479,7 +479,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let is_compatible_after_call = |lhs_ty, rhs_ty| { self.lookup_op_method( - lhs_ty, + (lhs_expr, lhs_ty), Some((rhs_expr, rhs_ty)), Op::Binary(op, is_assign), expected, @@ -578,7 +578,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // suggestion for the user. let errors = self .lookup_op_method( - lhs_ty, + (lhs_expr, lhs_ty), Some((rhs_expr, rhs_ty)), Op::Binary(op, is_assign), expected, @@ -779,7 +779,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected: Expectation<'tcx>, ) -> Ty<'tcx> { assert!(op.is_by_value()); - match self.lookup_op_method(operand_ty, None, Op::Unary(op, ex.span), expected) { + match self.lookup_op_method((ex, operand_ty), None, Op::Unary(op, ex.span), expected) { Ok(method) => { self.write_method_call_and_enforce_effects(ex.hir_id, ex.span, method); method.sig.output() @@ -865,7 +865,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn lookup_op_method( &self, - lhs_ty: Ty<'tcx>, + (lhs_expr, lhs_ty): (&'tcx hir::Expr<'tcx>, Ty<'tcx>), opt_rhs: Option<(&'tcx hir::Expr<'tcx>, Ty<'tcx>)>, op: Op, expected: Expectation<'tcx>, @@ -910,8 +910,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let cause = self.cause( span, traits::BinOp { + lhs_hir_id: lhs_expr.hir_id, + rhs_hir_id: opt_rhs_expr.map(|expr| expr.hir_id), rhs_span: opt_rhs_expr.map(|expr| expr.span), - is_lit: opt_rhs_expr.is_some_and(|expr| matches!(expr.kind, hir::ExprKind::Lit(_))), + rhs_is_lit: opt_rhs_expr + .is_some_and(|expr| matches!(expr.kind, hir::ExprKind::Lit(_))), output_ty: expected.only_has_type(self), }, ); diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index 5d0187a859833..178c9581a316b 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -429,8 +429,10 @@ pub enum ObligationCauseCode<'tcx> { MatchImpl(ObligationCause<'tcx>, DefId), BinOp { + lhs_hir_id: hir::HirId, + rhs_hir_id: Option, rhs_span: Option, - is_lit: bool, + rhs_is_lit: bool, output_ty: Option>, }, @@ -510,6 +512,21 @@ impl<'tcx> ObligationCauseCode<'tcx> { base_cause } + /// Returns the base obligation and the base trait predicate, if any, ignoring + /// derived obligations. + pub fn peel_derives_with_predicate(&self) -> (&Self, Option>) { + let mut base_cause = self; + let mut base_trait_pred = None; + while let Some((parent_code, parent_pred)) = base_cause.parent() { + base_cause = parent_code; + if let Some(parent_pred) = parent_pred { + base_trait_pred = Some(parent_pred); + } + } + + (base_cause, base_trait_pred) + } + pub fn parent(&self) -> Option<(&Self, Option>)> { match self { FunctionArgumentObligation { parent_code, .. } => Some((parent_code, None)), diff --git a/compiler/rustc_trait_selection/Cargo.toml b/compiler/rustc_trait_selection/Cargo.toml index 29c0d8b5ff172..1883099d345b9 100644 --- a/compiler/rustc_trait_selection/Cargo.toml +++ b/compiler/rustc_trait_selection/Cargo.toml @@ -5,6 +5,7 @@ edition = "2021" [dependencies] # tidy-alphabetical-start +itertools = "0.11.0" rustc_ast = { path = "../rustc_ast" } rustc_attr = { path = "../rustc_attr" } rustc_data_structures = { path = "../rustc_data_structures" } diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 9073cd6ac47a1..7d2feff350aed 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -47,6 +47,9 @@ use crate::traits::error_reporting::type_err_ctxt_ext::InferCtxtPrivExt; use crate::traits::query::evaluate_obligation::InferCtxtExt as _; use rustc_middle::ty::print::{with_forced_trimmed_paths, with_no_trimmed_paths}; +use itertools::EitherOrBoth; +use itertools::Itertools; + #[derive(Debug)] pub enum CoroutineInteriorOrUpvar { // span of interior type @@ -723,133 +726,276 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { err: &mut Diagnostic, trait_pred: ty::PolyTraitPredicate<'tcx>, ) -> bool { - // It only make sense when suggesting dereferences for arguments - let ObligationCauseCode::FunctionArgumentObligation { arg_hir_id, call_hir_id, .. } = - obligation.cause.code() - else { - return false; - }; - let Some(typeck_results) = &self.typeck_results else { - return false; - }; - let hir::Node::Expr(expr) = self.tcx.hir_node(*arg_hir_id) else { - return false; - }; - let Some(arg_ty) = typeck_results.expr_ty_adjusted_opt(expr) else { - return false; - }; - - let span = obligation.cause.span; - let mut real_trait_pred = trait_pred; let mut code = obligation.cause.code(); - while let Some((parent_code, parent_trait_pred)) = code.parent() { - code = parent_code; - if let Some(parent_trait_pred) = parent_trait_pred { - real_trait_pred = parent_trait_pred; - } + if let ObligationCauseCode::FunctionArgumentObligation { arg_hir_id, call_hir_id, .. } = + code + && let Some(typeck_results) = &self.typeck_results + && let hir::Node::Expr(expr) = self.tcx.hir_node(*arg_hir_id) + && let Some(arg_ty) = typeck_results.expr_ty_adjusted_opt(expr) + { + // Suggest dereferencing the argument to a function/method call if possible - // We `instantiate_bound_regions_with_erased` here because `make_subregion` does not handle - // `ReBound`, and we don't particularly care about the regions. - let real_ty = self.tcx.instantiate_bound_regions_with_erased(real_trait_pred.self_ty()); - if !self.can_eq(obligation.param_env, real_ty, arg_ty) { - continue; - } + let mut real_trait_pred = trait_pred; + while let Some((parent_code, parent_trait_pred)) = code.parent() { + code = parent_code; + if let Some(parent_trait_pred) = parent_trait_pred { + real_trait_pred = parent_trait_pred; + } - if let ty::Ref(region, base_ty, mutbl) = *real_ty.kind() { - let autoderef = (self.autoderef_steps)(base_ty); - if let Some(steps) = - autoderef.into_iter().enumerate().find_map(|(steps, (ty, obligations))| { - // Re-add the `&` - let ty = Ty::new_ref(self.tcx, region, TypeAndMut { ty, mutbl }); + // We `instantiate_bound_regions_with_erased` here because `make_subregion` does not handle + // `ReBound`, and we don't particularly care about the regions. + let real_ty = + self.tcx.instantiate_bound_regions_with_erased(real_trait_pred.self_ty()); + if self.can_eq(obligation.param_env, real_ty, arg_ty) + && let ty::Ref(region, base_ty, mutbl) = *real_ty.kind() + { + let autoderef = (self.autoderef_steps)(base_ty); + if let Some(steps) = + autoderef.into_iter().enumerate().find_map(|(steps, (ty, obligations))| { + // Re-add the `&` + let ty = Ty::new_ref(self.tcx, region, TypeAndMut { ty, mutbl }); + + // Remapping bound vars here + let real_trait_pred_and_ty = real_trait_pred + .map_bound(|inner_trait_pred| (inner_trait_pred, ty)); + let obligation = self.mk_trait_obligation_with_new_self_ty( + obligation.param_env, + real_trait_pred_and_ty, + ); + let may_hold = obligations + .iter() + .chain([&obligation]) + .all(|obligation| self.predicate_may_hold(obligation)) + .then_some(steps); + + may_hold + }) + { + if steps > 0 { + // Don't care about `&mut` because `DerefMut` is used less + // often and user will not expect that an autoderef happens. + if let Some(hir::Node::Expr(hir::Expr { + kind: + hir::ExprKind::AddrOf( + hir::BorrowKind::Ref, + hir::Mutability::Not, + expr, + ), + .. + })) = self.tcx.opt_hir_node(*arg_hir_id) + { + let derefs = "*".repeat(steps); + err.span_suggestion_verbose( + expr.span.shrink_to_lo(), + "consider dereferencing here", + derefs, + Applicability::MachineApplicable, + ); + return true; + } + } + } else if real_trait_pred != trait_pred { + // This branch addresses #87437. + + let span = obligation.cause.span; // Remapping bound vars here - let real_trait_pred_and_ty = - real_trait_pred.map_bound(|inner_trait_pred| (inner_trait_pred, ty)); + let real_trait_pred_and_base_ty = real_trait_pred + .map_bound(|inner_trait_pred| (inner_trait_pred, base_ty)); let obligation = self.mk_trait_obligation_with_new_self_ty( obligation.param_env, - real_trait_pred_and_ty, + real_trait_pred_and_base_ty, ); - let may_hold = obligations - .iter() - .chain([&obligation]) - .all(|obligation| self.predicate_may_hold(obligation)) - .then_some(steps); - - may_hold - }) - { - if steps > 0 { - // Don't care about `&mut` because `DerefMut` is used less - // often and user will not expect autoderef happens. - if let Some(hir::Node::Expr(hir::Expr { - kind: - hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, expr), - .. - })) = self.tcx.opt_hir_node(*arg_hir_id) + let sized_obligation = Obligation::new( + self.tcx, + obligation.cause.clone(), + obligation.param_env, + ty::TraitRef::from_lang_item( + self.tcx, + hir::LangItem::Sized, + obligation.cause.span, + [base_ty], + ), + ); + if self.predicate_may_hold(&obligation) + && self.predicate_must_hold_modulo_regions(&sized_obligation) { - let derefs = "*".repeat(steps); - err.span_suggestion_verbose( - expr.span.shrink_to_lo(), - "consider dereferencing here", - derefs, - Applicability::MachineApplicable, + let call_node = self.tcx.hir_node(*call_hir_id); + let msg = "consider dereferencing here"; + let is_receiver = matches!( + call_node, + Node::Expr(hir::Expr { + kind: hir::ExprKind::MethodCall(_, receiver_expr, ..), + .. + }) + if receiver_expr.hir_id == *arg_hir_id ); + if is_receiver { + err.multipart_suggestion_verbose( + msg, + vec![ + (span.shrink_to_lo(), "(*".to_string()), + (span.shrink_to_hi(), ")".to_string()), + ], + Applicability::MachineApplicable, + ) + } else { + err.span_suggestion_verbose( + span.shrink_to_lo(), + msg, + '*', + Applicability::MachineApplicable, + ) + }; return true; } } - } else if real_trait_pred != trait_pred { - // This branch addresses #87437. - - // Remapping bound vars here - let real_trait_pred_and_base_ty = - real_trait_pred.map_bound(|inner_trait_pred| (inner_trait_pred, base_ty)); + } + } + } else if let ( + ObligationCauseCode::BinOp { lhs_hir_id, rhs_hir_id: Some(rhs_hir_id), .. }, + predicate, + ) = code.peel_derives_with_predicate() + && let Some(typeck_results) = &self.typeck_results + && let hir::Node::Expr(lhs) = self.tcx.hir_node(*lhs_hir_id) + && let hir::Node::Expr(rhs) = self.tcx.hir_node(*rhs_hir_id) + && let Some(rhs_ty) = typeck_results.expr_ty_opt(rhs) + { + // Suggest dereferencing the LHS, RHS, or both terms of a binop if possible + + let trait_pred = predicate.unwrap_or(trait_pred); + let lhs_ty = self.tcx.instantiate_bound_regions_with_erased(trait_pred.self_ty()); + let lhs_autoderef = (self.autoderef_steps)(lhs_ty); + let rhs_autoderef = (self.autoderef_steps)(rhs_ty); + let first_lhs = lhs_autoderef.first().unwrap().clone(); + let first_rhs = rhs_autoderef.first().unwrap().clone(); + let mut autoderefs = lhs_autoderef + .into_iter() + .enumerate() + .rev() + .zip_longest(rhs_autoderef.into_iter().enumerate().rev()) + .map(|t| match t { + EitherOrBoth::Both(a, b) => (a, b), + EitherOrBoth::Left(a) => (a, (0, first_rhs.clone())), + EitherOrBoth::Right(b) => ((0, first_lhs.clone()), b), + }) + .rev(); + if let Some((lsteps, rsteps)) = + autoderefs.find_map(|((lsteps, (l_ty, _)), (rsteps, (r_ty, _)))| { + // Create a new predicate with the dereferenced LHS and RHS + // We simultaneously dereference both sides rather than doing them + // one at a time to account for cases such as &Box == &&T + let trait_pred_and_ty = trait_pred.map_bound(|inner| { + ( + ty::TraitPredicate { + trait_ref: ty::TraitRef::new( + self.tcx, + inner.trait_ref.def_id, + self.tcx.mk_args( + &[&[l_ty.into(), r_ty.into()], &inner.trait_ref.args[2..]] + .concat(), + ), + ), + ..inner + }, + l_ty, + ) + }); let obligation = self.mk_trait_obligation_with_new_self_ty( obligation.param_env, - real_trait_pred_and_base_ty, - ); - let sized_obligation = Obligation::new( - self.tcx, - obligation.cause.clone(), - obligation.param_env, - ty::TraitRef::from_lang_item( - self.tcx, - hir::LangItem::Sized, - obligation.cause.span, - [base_ty], - ), + trait_pred_and_ty, ); - if self.predicate_may_hold(&obligation) - && self.predicate_must_hold_modulo_regions(&sized_obligation) + self.predicate_may_hold(&obligation).then_some(match (lsteps, rsteps) { + (_, 0) => (Some(lsteps), None), + (0, _) => (None, Some(rsteps)), + _ => (Some(lsteps), Some(rsteps)), + }) + }) + { + let make_sugg = |mut expr: &Expr<'_>, mut steps| { + let mut prefix_span = expr.span.shrink_to_lo(); + let mut msg = "consider dereferencing here"; + if let hir::ExprKind::AddrOf(_, _, inner) = expr.kind { + msg = "consider removing the borrow and dereferencing instead"; + if let hir::ExprKind::AddrOf(..) = inner.kind { + msg = "consider removing the borrows and dereferencing instead"; + } + } + while let hir::ExprKind::AddrOf(_, _, inner) = expr.kind + && steps > 0 { - let call_node = self.tcx.hir_node(*call_hir_id); - let msg = "consider dereferencing here"; - let is_receiver = matches!( - call_node, - Node::Expr(hir::Expr { - kind: hir::ExprKind::MethodCall(_, receiver_expr, ..), - .. - }) - if receiver_expr.hir_id == *arg_hir_id + prefix_span = prefix_span.with_hi(inner.span.lo()); + expr = inner; + steps -= 1; + } + // Empty suggestions with empty spans ICE with debug assertions + if steps == 0 { + return ( + msg.trim_end_matches(" and dereferencing instead"), + vec![(prefix_span, String::new())], ); - if is_receiver { - err.multipart_suggestion_verbose( - msg, - vec![ - (span.shrink_to_lo(), "(*".to_string()), - (span.shrink_to_hi(), ")".to_string()), - ], - Applicability::MachineApplicable, - ) - } else { - err.span_suggestion_verbose( - span.shrink_to_lo(), - msg, - '*', - Applicability::MachineApplicable, - ) + } + let derefs = "*".repeat(steps); + let needs_parens = steps > 0 + && match expr.kind { + hir::ExprKind::Cast(_, _) | hir::ExprKind::Binary(_, _, _) => true, + _ if is_range_literal(expr) => true, + _ => false, }; - return true; + let mut suggestion = if needs_parens { + vec![ + ( + expr.span.with_lo(prefix_span.hi()).shrink_to_lo(), + format!("{derefs}("), + ), + (expr.span.shrink_to_hi(), ")".to_string()), + ] + } else { + vec![( + expr.span.with_lo(prefix_span.hi()).shrink_to_lo(), + format!("{derefs}"), + )] + }; + // Empty suggestions with empty spans ICE with debug assertions + if !prefix_span.is_empty() { + suggestion.push((prefix_span, String::new())); } + (msg, suggestion) + }; + + if let Some(lsteps) = lsteps + && let Some(rsteps) = rsteps + && lsteps > 0 + && rsteps > 0 + { + let mut suggestion = make_sugg(lhs, lsteps).1; + suggestion.append(&mut make_sugg(rhs, rsteps).1); + err.multipart_suggestion_verbose( + "consider dereferencing both sides of the expression", + suggestion, + Applicability::MachineApplicable, + ); + return true; + } else if let Some(lsteps) = lsteps + && lsteps > 0 + { + let (msg, suggestion) = make_sugg(lhs, lsteps); + err.multipart_suggestion_verbose( + msg, + suggestion, + Applicability::MachineApplicable, + ); + return true; + } else if let Some(rsteps) = rsteps + && rsteps > 0 + { + let (msg, suggestion) = make_sugg(rhs, rsteps); + err.multipart_suggestion_verbose( + msg, + suggestion, + Applicability::MachineApplicable, + ); + return true; } } } @@ -3591,7 +3737,9 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { trait_ref: &ty::PolyTraitRef<'tcx>, ) { let rhs_span = match obligation.cause.code() { - ObligationCauseCode::BinOp { rhs_span: Some(span), is_lit, .. } if *is_lit => span, + ObligationCauseCode::BinOp { rhs_span: Some(span), rhs_is_lit, .. } if *rhs_is_lit => { + span + } _ => return, }; if let ty::Float(_) = trait_ref.skip_binder().self_ty().kind() @@ -3694,6 +3842,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ); } } + fn note_function_argument_obligation( &self, body_id: LocalDefId, diff --git a/tests/ui/binop/binary-op-suggest-deref.fixed b/tests/ui/binop/binary-op-suggest-deref.fixed deleted file mode 100644 index 1ff3599137b16..0000000000000 --- a/tests/ui/binop/binary-op-suggest-deref.fixed +++ /dev/null @@ -1,8 +0,0 @@ -// Issue #52544 -// run-rustfix - -fn main() { - let i: &i64 = &1; - if *i < 0 {} - //~^ ERROR mismatched types [E0308] -} diff --git a/tests/ui/binop/binary-op-suggest-deref.rs b/tests/ui/binop/binary-op-suggest-deref.rs index 12505a9ac27e8..57f24a4c28ed4 100644 --- a/tests/ui/binop/binary-op-suggest-deref.rs +++ b/tests/ui/binop/binary-op-suggest-deref.rs @@ -1,8 +1,75 @@ -// Issue #52544 -// run-rustfix +#![allow(dead_code)] -fn main() { +fn foo() { + // Issue #52544 let i: &i64 = &1; if i < 0 {} //~^ ERROR mismatched types [E0308] } + +fn bar() { + // Issue #40660 + let foo = &&0; + + // Dereference LHS + _ = foo == 0; + //~^ERROR can't compare `&&{integer}` with `{integer}` [E0277] + _ = foo == &0; + //~^ERROR can't compare `&{integer}` with `{integer}` [E0277] + _ = &&&&foo == 0; + //~^ERROR can't compare `&&&&&&{integer}` with `{integer}` [E0277] + _ = *foo == 0; + //~^ERROR can't compare `&{integer}` with `{integer}` [E0277] + _ = &&foo == &&0; + //~^ERROR can't compare `&&{integer}` with `{integer}` [E0277] + _ = &Box::new(42) == 42; + //~^ERROR can't compare `&Box<{integer}>` with `{integer}` [E0277] + _ = &Box::new(&Box::new(&42)) == 42; + //~^ERROR can't compare `&Box<&Box<&{integer}>>` with `{integer}` [E0277] + + // Dereference RHS + _ = 0 == foo; + //~^ERROR can't compare `{integer}` with `&&{integer}` [E0277] + _ = &0 == foo; + //~^ERROR can't compare `{integer}` with `&{integer}` [E0277] + _ = 0 == &&&&foo; + //~^ERROR can't compare `{integer}` with `&&&&&&{integer}` [E0277] + _ = 0 == *foo; + //~^ERROR can't compare `{integer}` with `&{integer}` [E0277] + _ = &&0 == &&foo; + //~^ERROR can't compare `{integer}` with `&&{integer}` [E0277] + + // Dereference both sides + _ = &Box::new(Box::new(42)) == &foo; + //~^ERROR can't compare `Box>` with `&&{integer}` [E0277] + _ = &Box::new(42) == &foo; + //~^ERROR can't compare `Box<{integer}>` with `&&{integer}` [E0277] + _ = &Box::new(Box::new(Box::new(Box::new(42)))) == &foo; + //~^ERROR can't compare `Box>>>` with `&&{integer}` [E0277] + _ = &foo == &Box::new(Box::new(Box::new(Box::new(42)))); + //~^ERROR can't compare `&&{integer}` with `Box>>>` [E0277] + + // Don't suggest dereferencing the LHS; suggest boxing the RHS instead + _ = Box::new(42) == 42; + //~^ERROR mismatched types [E0308] + + // Don't suggest dereferencing with types that can't be compared + struct Foo; + _ = &&0 == Foo; + //~^ERROR can't compare `&&{integer}` with `Foo` [E0277] + _ = Foo == &&0; + //~^ERROR binary operation `==` cannot be applied to type `Foo` [E0369] +} + +fn baz() { + // Issue #44695 + let owned = "foo".to_owned(); + let string_ref = &owned; + let partial = "foobar"; + _ = string_ref == partial[..3]; + //~^ERROR can't compare `&String` with `str` [E0277] + _ = partial[..3] == string_ref; + //~^ERROR can't compare `str` with `&String` [E0277] +} + +fn main() {} diff --git a/tests/ui/binop/binary-op-suggest-deref.stderr b/tests/ui/binop/binary-op-suggest-deref.stderr index d1d0089ece795..f5de64e3ab1ae 100644 --- a/tests/ui/binop/binary-op-suggest-deref.stderr +++ b/tests/ui/binop/binary-op-suggest-deref.stderr @@ -9,6 +9,298 @@ help: consider dereferencing the borrow LL | if *i < 0 {} | + -error: aborting due to 1 previous error +error[E0277]: can't compare `&&{integer}` with `{integer}` + --> $DIR/binary-op-suggest-deref.rs:15:13 + | +LL | _ = foo == 0; + | ^^ no implementation for `&&{integer} == {integer}` + | + = help: the trait `PartialEq<{integer}>` is not implemented for `&&{integer}` +help: consider dereferencing here + | +LL | _ = **foo == 0; + | ++ + +error[E0277]: can't compare `&{integer}` with `{integer}` + --> $DIR/binary-op-suggest-deref.rs:17:13 + | +LL | _ = foo == &0; + | ^^ no implementation for `&{integer} == {integer}` + | + = help: the trait `PartialEq<{integer}>` is not implemented for `&{integer}` + = note: required for `&&{integer}` to implement `PartialEq<&{integer}>` +help: consider dereferencing here + | +LL | _ = *foo == &0; + | + + +error[E0277]: can't compare `&&&&&&{integer}` with `{integer}` + --> $DIR/binary-op-suggest-deref.rs:19:17 + | +LL | _ = &&&&foo == 0; + | ^^ no implementation for `&&&&&&{integer} == {integer}` + | + = help: the trait `PartialEq<{integer}>` is not implemented for `&&&&&&{integer}` +help: consider removing the borrows and dereferencing instead + | +LL - _ = &&&&foo == 0; +LL + _ = **foo == 0; + | + +error[E0277]: can't compare `&{integer}` with `{integer}` + --> $DIR/binary-op-suggest-deref.rs:21:14 + | +LL | _ = *foo == 0; + | ^^ no implementation for `&{integer} == {integer}` + | + = help: the trait `PartialEq<{integer}>` is not implemented for `&{integer}` +help: consider dereferencing here + | +LL | _ = **foo == 0; + | + + +error[E0277]: can't compare `&&{integer}` with `{integer}` + --> $DIR/binary-op-suggest-deref.rs:23:15 + | +LL | _ = &&foo == &&0; + | ^^ no implementation for `&&{integer} == {integer}` + | + = help: the trait `PartialEq<{integer}>` is not implemented for `&&{integer}` + = note: required for `&&&{integer}` to implement `PartialEq<&{integer}>` + = note: 1 redundant requirement hidden + = note: required for `&&&&{integer}` to implement `PartialEq<&&{integer}>` +help: consider removing the borrows + | +LL - _ = &&foo == &&0; +LL + _ = foo == &&0; + | + +error[E0277]: can't compare `&Box<{integer}>` with `{integer}` + --> $DIR/binary-op-suggest-deref.rs:25:23 + | +LL | _ = &Box::new(42) == 42; + | ^^ no implementation for `&Box<{integer}> == {integer}` + | + = help: the trait `PartialEq<{integer}>` is not implemented for `&Box<{integer}>` +help: consider removing the borrow and dereferencing instead + | +LL - _ = &Box::new(42) == 42; +LL + _ = *Box::new(42) == 42; + | + +error[E0277]: can't compare `&Box<&Box<&{integer}>>` with `{integer}` + --> $DIR/binary-op-suggest-deref.rs:27:35 + | +LL | _ = &Box::new(&Box::new(&42)) == 42; + | ^^ no implementation for `&Box<&Box<&{integer}>> == {integer}` + | + = help: the trait `PartialEq<{integer}>` is not implemented for `&Box<&Box<&{integer}>>` +help: consider removing the borrow and dereferencing instead + | +LL - _ = &Box::new(&Box::new(&42)) == 42; +LL + _ = ****Box::new(&Box::new(&42)) == 42; + | + +error[E0277]: can't compare `{integer}` with `&&{integer}` + --> $DIR/binary-op-suggest-deref.rs:31:11 + | +LL | _ = 0 == foo; + | ^^ no implementation for `{integer} == &&{integer}` + | + = help: the trait `PartialEq<&&{integer}>` is not implemented for `{integer}` +help: consider dereferencing here + | +LL | _ = 0 == **foo; + | ++ + +error[E0277]: can't compare `{integer}` with `&{integer}` + --> $DIR/binary-op-suggest-deref.rs:33:12 + | +LL | _ = &0 == foo; + | ^^ no implementation for `{integer} == &{integer}` + | + = help: the trait `PartialEq<&{integer}>` is not implemented for `{integer}` + = note: required for `&{integer}` to implement `PartialEq<&&{integer}>` +help: consider dereferencing here + | +LL | _ = &0 == *foo; + | + + +error[E0277]: can't compare `{integer}` with `&&&&&&{integer}` + --> $DIR/binary-op-suggest-deref.rs:35:11 + | +LL | _ = 0 == &&&&foo; + | ^^ no implementation for `{integer} == &&&&&&{integer}` + | + = help: the trait `PartialEq<&&&&&&{integer}>` is not implemented for `{integer}` +help: consider removing the borrows and dereferencing instead + | +LL - _ = 0 == &&&&foo; +LL + _ = 0 == **foo; + | + +error[E0277]: can't compare `{integer}` with `&{integer}` + --> $DIR/binary-op-suggest-deref.rs:37:11 + | +LL | _ = 0 == *foo; + | ^^ no implementation for `{integer} == &{integer}` + | + = help: the trait `PartialEq<&{integer}>` is not implemented for `{integer}` +help: consider dereferencing here + | +LL | _ = 0 == **foo; + | + + +error[E0277]: can't compare `{integer}` with `&&{integer}` + --> $DIR/binary-op-suggest-deref.rs:39:13 + | +LL | _ = &&0 == &&foo; + | ^^ no implementation for `{integer} == &&{integer}` + | + = help: the trait `PartialEq<&&{integer}>` is not implemented for `{integer}` + = note: required for `&{integer}` to implement `PartialEq<&&&{integer}>` + = note: 1 redundant requirement hidden + = note: required for `&&{integer}` to implement `PartialEq<&&&&{integer}>` +help: consider removing the borrows + | +LL - _ = &&0 == &&foo; +LL + _ = &&0 == foo; + | + +error[E0277]: can't compare `Box>` with `&&{integer}` + --> $DIR/binary-op-suggest-deref.rs:43:33 + | +LL | _ = &Box::new(Box::new(42)) == &foo; + | ^^ no implementation for `Box> == &&{integer}` + | + = help: the trait `PartialEq<&&{integer}>` is not implemented for `Box>` + = note: required for `&Box>` to implement `PartialEq<&&&{integer}>` +help: consider dereferencing both sides of the expression + | +LL - _ = &Box::new(Box::new(42)) == &foo; +LL + _ = **Box::new(Box::new(42)) == **foo; + | + +error[E0277]: can't compare `Box<{integer}>` with `&&{integer}` + --> $DIR/binary-op-suggest-deref.rs:45:23 + | +LL | _ = &Box::new(42) == &foo; + | ^^ no implementation for `Box<{integer}> == &&{integer}` + | + = help: the trait `PartialEq<&&{integer}>` is not implemented for `Box<{integer}>` + = note: required for `&Box<{integer}>` to implement `PartialEq<&&&{integer}>` +help: consider dereferencing both sides of the expression + | +LL - _ = &Box::new(42) == &foo; +LL + _ = *Box::new(42) == **foo; + | + +error[E0277]: can't compare `Box>>>` with `&&{integer}` + --> $DIR/binary-op-suggest-deref.rs:47:53 + | +LL | _ = &Box::new(Box::new(Box::new(Box::new(42)))) == &foo; + | ^^ no implementation for `Box>>> == &&{integer}` + | + = help: the trait `PartialEq<&&{integer}>` is not implemented for `Box>>>` + = note: required for `&Box>>>` to implement `PartialEq<&&&{integer}>` +help: consider dereferencing both sides of the expression + | +LL - _ = &Box::new(Box::new(Box::new(Box::new(42)))) == &foo; +LL + _ = ****Box::new(Box::new(Box::new(Box::new(42)))) == **foo; + | + +error[E0277]: can't compare `&&{integer}` with `Box>>>` + --> $DIR/binary-op-suggest-deref.rs:49:14 + | +LL | _ = &foo == &Box::new(Box::new(Box::new(Box::new(42)))); + | ^^ no implementation for `&&{integer} == Box>>>` + | + = help: the trait `PartialEq>>>>` is not implemented for `&&{integer}` + = note: required for `&&&{integer}` to implement `PartialEq<&Box>>>>` +help: consider dereferencing both sides of the expression + | +LL - _ = &foo == &Box::new(Box::new(Box::new(Box::new(42)))); +LL + _ = **foo == ****Box::new(Box::new(Box::new(Box::new(42)))); + | + +error[E0308]: mismatched types + --> $DIR/binary-op-suggest-deref.rs:53:25 + | +LL | _ = Box::new(42) == 42; + | ------------ ^^ expected `Box<{integer}>`, found integer + | | + | expected because this is `Box<{integer}>` + | + = note: expected struct `Box<{integer}>` + found type `{integer}` + = note: for more on the distinction between the stack and the heap, read https://doc.rust-lang.org/book/ch15-01-box.html, https://doc.rust-lang.org/rust-by-example/std/box.html, and https://doc.rust-lang.org/std/boxed/index.html +help: store this in the heap by calling `Box::new` + | +LL | _ = Box::new(42) == Box::new(42); + | +++++++++ + + +error[E0277]: can't compare `&&{integer}` with `Foo` + --> $DIR/binary-op-suggest-deref.rs:58:13 + | +LL | _ = &&0 == Foo; + | ^^ no implementation for `&&{integer} == Foo` + | + = help: the trait `PartialEq` is not implemented for `&&{integer}` + = help: the following other types implement trait `PartialEq`: + isize + i8 + i16 + i32 + i64 + i128 + usize + u8 + and 6 others + +error[E0369]: binary operation `==` cannot be applied to type `Foo` + --> $DIR/binary-op-suggest-deref.rs:60:13 + | +LL | _ = Foo == &&0; + | --- ^^ --- &&{integer} + | | + | Foo + | +note: an implementation of `PartialEq<&&{integer}>` might be missing for `Foo` + --> $DIR/binary-op-suggest-deref.rs:57:5 + | +LL | struct Foo; + | ^^^^^^^^^^ must implement `PartialEq<&&{integer}>` +help: consider annotating `Foo` with `#[derive(PartialEq)]` + | +LL + #[derive(PartialEq)] +LL | struct Foo; + | + +error[E0277]: can't compare `&String` with `str` + --> $DIR/binary-op-suggest-deref.rs:69:20 + | +LL | _ = string_ref == partial[..3]; + | ^^ no implementation for `&String == str` + | + = help: the trait `PartialEq` is not implemented for `&String` +help: consider dereferencing here + | +LL | _ = *string_ref == partial[..3]; + | + + +error[E0277]: can't compare `str` with `&String` + --> $DIR/binary-op-suggest-deref.rs:71:22 + | +LL | _ = partial[..3] == string_ref; + | ^^ no implementation for `str == &String` + | + = help: the trait `PartialEq<&String>` is not implemented for `str` +help: consider dereferencing here + | +LL | _ = partial[..3] == *string_ref; + | + + +error: aborting due to 22 previous errors -For more information about this error, try `rustc --explain E0308`. +Some errors have detailed explanations: E0277, E0308, E0369. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/dst/issue-113447.fixed b/tests/ui/dst/issue-113447.fixed deleted file mode 100644 index 536f680f697c7..0000000000000 --- a/tests/ui/dst/issue-113447.fixed +++ /dev/null @@ -1,25 +0,0 @@ -// run-rustfix - -pub struct Bytes; - -impl Bytes { - pub fn as_slice(&self) -> &[u8] { - todo!() - } -} - -impl PartialEq<[u8]> for Bytes { - fn eq(&self, other: &[u8]) -> bool { - self.as_slice() == other - } -} - -impl PartialEq for &[u8] { - fn eq(&self, other: &Bytes) -> bool { - *other == **self - } -} - -fn main() { - let _ = &[0u8] == &[0xAA][..]; //~ ERROR can't compare `&[u8; 1]` with `[{integer}; 1]` -} diff --git a/tests/ui/dst/issue-113447.rs b/tests/ui/dst/issue-113447.rs index c10a4f2ff8ec4..75156a117e996 100644 --- a/tests/ui/dst/issue-113447.rs +++ b/tests/ui/dst/issue-113447.rs @@ -1,5 +1,3 @@ -// run-rustfix - pub struct Bytes; impl Bytes { diff --git a/tests/ui/dst/issue-113447.stderr b/tests/ui/dst/issue-113447.stderr index 266eb228046a2..4d0ed33a643a8 100644 --- a/tests/ui/dst/issue-113447.stderr +++ b/tests/ui/dst/issue-113447.stderr @@ -1,24 +1,15 @@ error[E0277]: can't compare `&[u8; 1]` with `[{integer}; 1]` - --> $DIR/issue-113447.rs:24:20 + --> $DIR/issue-113447.rs:22:20 | LL | let _ = &[0u8] == [0xAA]; | ^^ no implementation for `&[u8; 1] == [{integer}; 1]` | = help: the trait `PartialEq<[{integer}; 1]>` is not implemented for `&[u8; 1]` - = help: the following other types implement trait `PartialEq`: - <[A; N] as PartialEq<[B; N]>> - <[A; N] as PartialEq<[B]>> - <[A; N] as PartialEq<&[B]>> - <[A; N] as PartialEq<&mut [B]>> - <[T] as PartialEq>> - <[A] as PartialEq<[B]>> - <[B] as PartialEq<[A; N]>> - <&[u8] as PartialEq> - and 4 others -help: convert the array to a `&[u8]` slice instead +help: consider removing the borrow + | +LL - let _ = &[0u8] == [0xAA]; +LL + let _ = [0u8] == [0xAA]; | -LL | let _ = &[0u8] == &[0xAA][..]; - | + ++++ error: aborting due to 1 previous error diff --git a/tests/ui/partialeq_help.stderr b/tests/ui/partialeq_help.stderr index fdff94f425c8a..f5de1308e8714 100644 --- a/tests/ui/partialeq_help.stderr +++ b/tests/ui/partialeq_help.stderr @@ -5,6 +5,10 @@ LL | a == b; | ^^ no implementation for `&T == T` | = help: the trait `PartialEq` is not implemented for `&T` +help: consider dereferencing here + | +LL | *a == b; + | + help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement | LL | fn foo(a: &T, b: T) where &T: PartialEq { @@ -17,6 +21,10 @@ LL | a == b; | ^^ no implementation for `&T == T` | = help: the trait `PartialEq` is not implemented for `&T` +help: consider dereferencing here + | +LL | *a == b; + | + help: consider extending the `where` clause, but there might be an alternative better way to express this requirement | LL | fn foo2(a: &T, b: T) where &T: PartialEq {