Provide better suggestions for T == &T and &T == T
This commit is contained in:
parent
de686cbc65
commit
2618e0f805
13 changed files with 670 additions and 176 deletions
|
@ -4580,6 +4580,7 @@ checksum = "8ba09476327c4b70ccefb6180f046ef588c26a24cf5d269a9feba316eb4f029f"
|
||||||
name = "rustc_trait_selection"
|
name = "rustc_trait_selection"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"itertools",
|
||||||
"rustc_ast",
|
"rustc_ast",
|
||||||
"rustc_attr",
|
"rustc_attr",
|
||||||
"rustc_data_structures",
|
"rustc_data_structures",
|
||||||
|
|
|
@ -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 {
|
if let Some(hir::Node::Expr(hir::Expr {
|
||||||
kind: hir::ExprKind::Binary(_, lhs, ..),
|
kind: hir::ExprKind::Binary(_, lhs, ..),
|
||||||
..
|
..
|
||||||
|
|
|
@ -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 let Some(lhs_deref_ty) = self.deref_once_mutably_for_diagnostic(lhs_ty) {
|
||||||
if self
|
if self
|
||||||
.lookup_op_method(
|
.lookup_op_method(
|
||||||
lhs_deref_ty,
|
(lhs, lhs_deref_ty),
|
||||||
Some((rhs, rhs_ty)),
|
Some((rhs, rhs_ty)),
|
||||||
Op::Binary(op, IsAssign::Yes),
|
Op::Binary(op, IsAssign::Yes),
|
||||||
expected,
|
expected,
|
||||||
|
@ -58,7 +58,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
// emitted a better suggestion during error handling in check_overloaded_binop.
|
// emitted a better suggestion during error handling in check_overloaded_binop.
|
||||||
if self
|
if self
|
||||||
.lookup_op_method(
|
.lookup_op_method(
|
||||||
lhs_ty,
|
(lhs, lhs_ty),
|
||||||
Some((rhs, rhs_ty)),
|
Some((rhs, rhs_ty)),
|
||||||
Op::Binary(op, IsAssign::Yes),
|
Op::Binary(op, IsAssign::Yes),
|
||||||
expected,
|
expected,
|
||||||
|
@ -246,7 +246,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
});
|
});
|
||||||
|
|
||||||
let result = self.lookup_op_method(
|
let result = self.lookup_op_method(
|
||||||
lhs_ty,
|
(lhs_expr, lhs_ty),
|
||||||
Some((rhs_expr, rhs_ty_var)),
|
Some((rhs_expr, rhs_ty_var)),
|
||||||
Op::Binary(op, is_assign),
|
Op::Binary(op, is_assign),
|
||||||
expected,
|
expected,
|
||||||
|
@ -391,7 +391,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
|err: &mut DiagnosticBuilder<'_, _>, lhs_deref_ty: Ty<'tcx>| {
|
|err: &mut DiagnosticBuilder<'_, _>, lhs_deref_ty: Ty<'tcx>| {
|
||||||
if self
|
if self
|
||||||
.lookup_op_method(
|
.lookup_op_method(
|
||||||
lhs_deref_ty,
|
(lhs_expr, lhs_deref_ty),
|
||||||
Some((rhs_expr, rhs_ty)),
|
Some((rhs_expr, rhs_ty)),
|
||||||
Op::Binary(op, is_assign),
|
Op::Binary(op, is_assign),
|
||||||
expected,
|
expected,
|
||||||
|
@ -424,7 +424,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
rhs_new_mutbl: Option<ast::Mutability>| {
|
rhs_new_mutbl: Option<ast::Mutability>| {
|
||||||
if self
|
if self
|
||||||
.lookup_op_method(
|
.lookup_op_method(
|
||||||
lhs_adjusted_ty,
|
(lhs_expr, lhs_adjusted_ty),
|
||||||
Some((rhs_expr, rhs_adjusted_ty)),
|
Some((rhs_expr, rhs_adjusted_ty)),
|
||||||
Op::Binary(op, is_assign),
|
Op::Binary(op, is_assign),
|
||||||
expected,
|
expected,
|
||||||
|
@ -479,7 +479,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
|
|
||||||
let is_compatible_after_call = |lhs_ty, rhs_ty| {
|
let is_compatible_after_call = |lhs_ty, rhs_ty| {
|
||||||
self.lookup_op_method(
|
self.lookup_op_method(
|
||||||
lhs_ty,
|
(lhs_expr, lhs_ty),
|
||||||
Some((rhs_expr, rhs_ty)),
|
Some((rhs_expr, rhs_ty)),
|
||||||
Op::Binary(op, is_assign),
|
Op::Binary(op, is_assign),
|
||||||
expected,
|
expected,
|
||||||
|
@ -578,7 +578,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
// suggestion for the user.
|
// suggestion for the user.
|
||||||
let errors = self
|
let errors = self
|
||||||
.lookup_op_method(
|
.lookup_op_method(
|
||||||
lhs_ty,
|
(lhs_expr, lhs_ty),
|
||||||
Some((rhs_expr, rhs_ty)),
|
Some((rhs_expr, rhs_ty)),
|
||||||
Op::Binary(op, is_assign),
|
Op::Binary(op, is_assign),
|
||||||
expected,
|
expected,
|
||||||
|
@ -779,7 +779,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
expected: Expectation<'tcx>,
|
expected: Expectation<'tcx>,
|
||||||
) -> Ty<'tcx> {
|
) -> Ty<'tcx> {
|
||||||
assert!(op.is_by_value());
|
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) => {
|
Ok(method) => {
|
||||||
self.write_method_call_and_enforce_effects(ex.hir_id, ex.span, method);
|
self.write_method_call_and_enforce_effects(ex.hir_id, ex.span, method);
|
||||||
method.sig.output()
|
method.sig.output()
|
||||||
|
@ -865,7 +865,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
|
|
||||||
fn lookup_op_method(
|
fn lookup_op_method(
|
||||||
&self,
|
&self,
|
||||||
lhs_ty: Ty<'tcx>,
|
(lhs_expr, lhs_ty): (&'tcx hir::Expr<'tcx>, Ty<'tcx>),
|
||||||
opt_rhs: Option<(&'tcx hir::Expr<'tcx>, Ty<'tcx>)>,
|
opt_rhs: Option<(&'tcx hir::Expr<'tcx>, Ty<'tcx>)>,
|
||||||
op: Op,
|
op: Op,
|
||||||
expected: Expectation<'tcx>,
|
expected: Expectation<'tcx>,
|
||||||
|
@ -910,8 +910,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
let cause = self.cause(
|
let cause = self.cause(
|
||||||
span,
|
span,
|
||||||
traits::BinOp {
|
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),
|
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),
|
output_ty: expected.only_has_type(self),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
@ -429,8 +429,10 @@ pub enum ObligationCauseCode<'tcx> {
|
||||||
MatchImpl(ObligationCause<'tcx>, DefId),
|
MatchImpl(ObligationCause<'tcx>, DefId),
|
||||||
|
|
||||||
BinOp {
|
BinOp {
|
||||||
|
lhs_hir_id: hir::HirId,
|
||||||
|
rhs_hir_id: Option<hir::HirId>,
|
||||||
rhs_span: Option<Span>,
|
rhs_span: Option<Span>,
|
||||||
is_lit: bool,
|
rhs_is_lit: bool,
|
||||||
output_ty: Option<Ty<'tcx>>,
|
output_ty: Option<Ty<'tcx>>,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -510,6 +512,21 @@ impl<'tcx> ObligationCauseCode<'tcx> {
|
||||||
base_cause
|
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<ty::PolyTraitPredicate<'tcx>>) {
|
||||||
|
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<ty::PolyTraitPredicate<'tcx>>)> {
|
pub fn parent(&self) -> Option<(&Self, Option<ty::PolyTraitPredicate<'tcx>>)> {
|
||||||
match self {
|
match self {
|
||||||
FunctionArgumentObligation { parent_code, .. } => Some((parent_code, None)),
|
FunctionArgumentObligation { parent_code, .. } => Some((parent_code, None)),
|
||||||
|
|
|
@ -5,6 +5,7 @@ edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# tidy-alphabetical-start
|
# tidy-alphabetical-start
|
||||||
|
itertools = "0.11.0"
|
||||||
rustc_ast = { path = "../rustc_ast" }
|
rustc_ast = { path = "../rustc_ast" }
|
||||||
rustc_attr = { path = "../rustc_attr" }
|
rustc_attr = { path = "../rustc_attr" }
|
||||||
rustc_data_structures = { path = "../rustc_data_structures" }
|
rustc_data_structures = { path = "../rustc_data_structures" }
|
||||||
|
|
|
@ -47,6 +47,9 @@ use crate::traits::error_reporting::type_err_ctxt_ext::InferCtxtPrivExt;
|
||||||
use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
|
use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
|
||||||
use rustc_middle::ty::print::{with_forced_trimmed_paths, with_no_trimmed_paths};
|
use rustc_middle::ty::print::{with_forced_trimmed_paths, with_no_trimmed_paths};
|
||||||
|
|
||||||
|
use itertools::EitherOrBoth;
|
||||||
|
use itertools::Itertools;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum CoroutineInteriorOrUpvar {
|
pub enum CoroutineInteriorOrUpvar {
|
||||||
// span of interior type
|
// span of interior type
|
||||||
|
@ -723,133 +726,276 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|
||||||
err: &mut Diagnostic,
|
err: &mut Diagnostic,
|
||||||
trait_pred: ty::PolyTraitPredicate<'tcx>,
|
trait_pred: ty::PolyTraitPredicate<'tcx>,
|
||||||
) -> bool {
|
) -> 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();
|
let mut code = obligation.cause.code();
|
||||||
while let Some((parent_code, parent_trait_pred)) = code.parent() {
|
if let ObligationCauseCode::FunctionArgumentObligation { arg_hir_id, call_hir_id, .. } =
|
||||||
code = parent_code;
|
code
|
||||||
if let Some(parent_trait_pred) = parent_trait_pred {
|
&& let Some(typeck_results) = &self.typeck_results
|
||||||
real_trait_pred = parent_trait_pred;
|
&& 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
|
let mut real_trait_pred = trait_pred;
|
||||||
// `ReBound`, and we don't particularly care about the regions.
|
while let Some((parent_code, parent_trait_pred)) = code.parent() {
|
||||||
let real_ty = self.tcx.instantiate_bound_regions_with_erased(real_trait_pred.self_ty());
|
code = parent_code;
|
||||||
if !self.can_eq(obligation.param_env, real_ty, arg_ty) {
|
if let Some(parent_trait_pred) = parent_trait_pred {
|
||||||
continue;
|
real_trait_pred = parent_trait_pred;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let ty::Ref(region, base_ty, mutbl) = *real_ty.kind() {
|
// We `instantiate_bound_regions_with_erased` here because `make_subregion` does not handle
|
||||||
let autoderef = (self.autoderef_steps)(base_ty);
|
// `ReBound`, and we don't particularly care about the regions.
|
||||||
if let Some(steps) =
|
let real_ty =
|
||||||
autoderef.into_iter().enumerate().find_map(|(steps, (ty, obligations))| {
|
self.tcx.instantiate_bound_regions_with_erased(real_trait_pred.self_ty());
|
||||||
// Re-add the `&`
|
|
||||||
let ty = Ty::new_ref(self.tcx, region, TypeAndMut { ty, mutbl });
|
|
||||||
|
|
||||||
|
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
|
// Remapping bound vars here
|
||||||
let real_trait_pred_and_ty =
|
let real_trait_pred_and_base_ty = real_trait_pred
|
||||||
real_trait_pred.map_bound(|inner_trait_pred| (inner_trait_pred, ty));
|
.map_bound(|inner_trait_pred| (inner_trait_pred, base_ty));
|
||||||
let obligation = self.mk_trait_obligation_with_new_self_ty(
|
let obligation = self.mk_trait_obligation_with_new_self_ty(
|
||||||
obligation.param_env,
|
obligation.param_env,
|
||||||
real_trait_pred_and_ty,
|
real_trait_pred_and_base_ty,
|
||||||
);
|
);
|
||||||
let may_hold = obligations
|
let sized_obligation = Obligation::new(
|
||||||
.iter()
|
self.tcx,
|
||||||
.chain([&obligation])
|
obligation.cause.clone(),
|
||||||
.all(|obligation| self.predicate_may_hold(obligation))
|
obligation.param_env,
|
||||||
.then_some(steps);
|
ty::TraitRef::from_lang_item(
|
||||||
|
self.tcx,
|
||||||
may_hold
|
hir::LangItem::Sized,
|
||||||
})
|
obligation.cause.span,
|
||||||
{
|
[base_ty],
|
||||||
if steps > 0 {
|
),
|
||||||
// Don't care about `&mut` because `DerefMut` is used less
|
);
|
||||||
// often and user will not expect autoderef happens.
|
if self.predicate_may_hold(&obligation)
|
||||||
if let Some(hir::Node::Expr(hir::Expr {
|
&& self.predicate_must_hold_modulo_regions(&sized_obligation)
|
||||||
kind:
|
|
||||||
hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, expr),
|
|
||||||
..
|
|
||||||
})) = self.tcx.opt_hir_node(*arg_hir_id)
|
|
||||||
{
|
{
|
||||||
let derefs = "*".repeat(steps);
|
let call_node = self.tcx.hir_node(*call_hir_id);
|
||||||
err.span_suggestion_verbose(
|
let msg = "consider dereferencing here";
|
||||||
expr.span.shrink_to_lo(),
|
let is_receiver = matches!(
|
||||||
"consider dereferencing here",
|
call_node,
|
||||||
derefs,
|
Node::Expr(hir::Expr {
|
||||||
Applicability::MachineApplicable,
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if real_trait_pred != trait_pred {
|
}
|
||||||
// This branch addresses #87437.
|
}
|
||||||
|
} 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
|
||||||
|
|
||||||
// Remapping bound vars here
|
let trait_pred = predicate.unwrap_or(trait_pred);
|
||||||
let real_trait_pred_and_base_ty =
|
let lhs_ty = self.tcx.instantiate_bound_regions_with_erased(trait_pred.self_ty());
|
||||||
real_trait_pred.map_bound(|inner_trait_pred| (inner_trait_pred, base_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> == &&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(
|
let obligation = self.mk_trait_obligation_with_new_self_ty(
|
||||||
obligation.param_env,
|
obligation.param_env,
|
||||||
real_trait_pred_and_base_ty,
|
trait_pred_and_ty,
|
||||||
);
|
);
|
||||||
let sized_obligation = Obligation::new(
|
self.predicate_may_hold(&obligation).then_some(match (lsteps, rsteps) {
|
||||||
self.tcx,
|
(_, 0) => (Some(lsteps), None),
|
||||||
obligation.cause.clone(),
|
(0, _) => (None, Some(rsteps)),
|
||||||
obligation.param_env,
|
_ => (Some(lsteps), Some(rsteps)),
|
||||||
ty::TraitRef::from_lang_item(
|
})
|
||||||
self.tcx,
|
})
|
||||||
hir::LangItem::Sized,
|
{
|
||||||
obligation.cause.span,
|
let make_sugg = |mut expr: &Expr<'_>, mut steps| {
|
||||||
[base_ty],
|
let mut prefix_span = expr.span.shrink_to_lo();
|
||||||
),
|
let mut msg = "consider dereferencing here";
|
||||||
);
|
if let hir::ExprKind::AddrOf(_, _, inner) = expr.kind {
|
||||||
if self.predicate_may_hold(&obligation)
|
msg = "consider removing the borrow and dereferencing instead";
|
||||||
&& self.predicate_must_hold_modulo_regions(&sized_obligation)
|
if let hir::ExprKind::AddrOf(..) = inner.kind {
|
||||||
{
|
msg = "consider removing the borrows and dereferencing instead";
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
while let hir::ExprKind::AddrOf(_, _, inner) = expr.kind
|
||||||
|
&& steps > 0
|
||||||
|
{
|
||||||
|
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())],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
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,
|
||||||
|
};
|
||||||
|
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>,
|
trait_ref: &ty::PolyTraitRef<'tcx>,
|
||||||
) {
|
) {
|
||||||
let rhs_span = match obligation.cause.code() {
|
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,
|
_ => return,
|
||||||
};
|
};
|
||||||
if let ty::Float(_) = trait_ref.skip_binder().self_ty().kind()
|
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(
|
fn note_function_argument_obligation(
|
||||||
&self,
|
&self,
|
||||||
body_id: LocalDefId,
|
body_id: LocalDefId,
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
// Issue #52544
|
|
||||||
// run-rustfix
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let i: &i64 = &1;
|
|
||||||
if *i < 0 {}
|
|
||||||
//~^ ERROR mismatched types [E0308]
|
|
||||||
}
|
|
|
@ -1,8 +1,75 @@
|
||||||
// Issue #52544
|
#![allow(dead_code)]
|
||||||
// run-rustfix
|
|
||||||
|
|
||||||
fn main() {
|
fn foo() {
|
||||||
|
// Issue #52544
|
||||||
let i: &i64 = &1;
|
let i: &i64 = &1;
|
||||||
if i < 0 {}
|
if i < 0 {}
|
||||||
//~^ ERROR mismatched types [E0308]
|
//~^ 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<Box<{integer}>>` 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<Box<Box<Box<{integer}>>>>` with `&&{integer}` [E0277]
|
||||||
|
_ = &foo == &Box::new(Box::new(Box::new(Box::new(42))));
|
||||||
|
//~^ERROR can't compare `&&{integer}` with `Box<Box<Box<Box<{integer}>>>>` [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() {}
|
||||||
|
|
|
@ -9,6 +9,298 @@ help: consider dereferencing the borrow
|
||||||
LL | if *i < 0 {}
|
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;
|
||||||
|
| ++
|
||||||
|
|
||||||
For more information about this error, try `rustc --explain E0308`.
|
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<Box<{integer}>>` with `&&{integer}`
|
||||||
|
--> $DIR/binary-op-suggest-deref.rs:43:33
|
||||||
|
|
|
||||||
|
LL | _ = &Box::new(Box::new(42)) == &foo;
|
||||||
|
| ^^ no implementation for `Box<Box<{integer}>> == &&{integer}`
|
||||||
|
|
|
||||||
|
= help: the trait `PartialEq<&&{integer}>` is not implemented for `Box<Box<{integer}>>`
|
||||||
|
= note: required for `&Box<Box<{integer}>>` 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<Box<Box<Box<{integer}>>>>` 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<Box<Box<Box<{integer}>>>> == &&{integer}`
|
||||||
|
|
|
||||||
|
= help: the trait `PartialEq<&&{integer}>` is not implemented for `Box<Box<Box<Box<{integer}>>>>`
|
||||||
|
= note: required for `&Box<Box<Box<Box<{integer}>>>>` 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<Box<Box<Box<{integer}>>>>`
|
||||||
|
--> $DIR/binary-op-suggest-deref.rs:49:14
|
||||||
|
|
|
||||||
|
LL | _ = &foo == &Box::new(Box::new(Box::new(Box::new(42))));
|
||||||
|
| ^^ no implementation for `&&{integer} == Box<Box<Box<Box<{integer}>>>>`
|
||||||
|
|
|
||||||
|
= help: the trait `PartialEq<Box<Box<Box<Box<{integer}>>>>>` is not implemented for `&&{integer}`
|
||||||
|
= note: required for `&&&{integer}` to implement `PartialEq<&Box<Box<Box<Box<{integer}>>>>>`
|
||||||
|
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<Foo>` is not implemented for `&&{integer}`
|
||||||
|
= help: the following other types implement trait `PartialEq<Rhs>`:
|
||||||
|
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<str>` 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
|
||||||
|
|
||||||
|
Some errors have detailed explanations: E0277, E0308, E0369.
|
||||||
|
For more information about an error, try `rustc --explain E0277`.
|
||||||
|
|
|
@ -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<Bytes> for &[u8] {
|
|
||||||
fn eq(&self, other: &Bytes) -> bool {
|
|
||||||
*other == **self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let _ = &[0u8] == &[0xAA][..]; //~ ERROR can't compare `&[u8; 1]` with `[{integer}; 1]`
|
|
||||||
}
|
|
|
@ -1,5 +1,3 @@
|
||||||
// run-rustfix
|
|
||||||
|
|
||||||
pub struct Bytes;
|
pub struct Bytes;
|
||||||
|
|
||||||
impl Bytes {
|
impl Bytes {
|
||||||
|
|
|
@ -1,24 +1,15 @@
|
||||||
error[E0277]: can't compare `&[u8; 1]` with `[{integer}; 1]`
|
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];
|
LL | let _ = &[0u8] == [0xAA];
|
||||||
| ^^ no implementation for `&[u8; 1] == [{integer}; 1]`
|
| ^^ no implementation for `&[u8; 1] == [{integer}; 1]`
|
||||||
|
|
|
|
||||||
= help: the trait `PartialEq<[{integer}; 1]>` is not implemented for `&[u8; 1]`
|
= help: the trait `PartialEq<[{integer}; 1]>` is not implemented for `&[u8; 1]`
|
||||||
= help: the following other types implement trait `PartialEq<Rhs>`:
|
help: consider removing the borrow
|
||||||
<[A; N] as PartialEq<[B; N]>>
|
|
|
||||||
<[A; N] as PartialEq<[B]>>
|
LL - let _ = &[0u8] == [0xAA];
|
||||||
<[A; N] as PartialEq<&[B]>>
|
LL + let _ = [0u8] == [0xAA];
|
||||||
<[A; N] as PartialEq<&mut [B]>>
|
|
||||||
<[T] as PartialEq<Vec<U, A>>>
|
|
||||||
<[A] as PartialEq<[B]>>
|
|
||||||
<[B] as PartialEq<[A; N]>>
|
|
||||||
<&[u8] as PartialEq<Bytes>>
|
|
||||||
and 4 others
|
|
||||||
help: convert the array to a `&[u8]` slice instead
|
|
||||||
|
|
|
|
||||||
LL | let _ = &[0u8] == &[0xAA][..];
|
|
||||||
| + ++++
|
|
||||||
|
|
||||||
error: aborting due to 1 previous error
|
error: aborting due to 1 previous error
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,10 @@ LL | a == b;
|
||||||
| ^^ no implementation for `&T == T`
|
| ^^ no implementation for `&T == T`
|
||||||
|
|
|
|
||||||
= help: the trait `PartialEq<T>` is not implemented for `&T`
|
= help: the trait `PartialEq<T>` 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
|
help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement
|
||||||
|
|
|
|
||||||
LL | fn foo<T: PartialEq>(a: &T, b: T) where &T: PartialEq<T> {
|
LL | fn foo<T: PartialEq>(a: &T, b: T) where &T: PartialEq<T> {
|
||||||
|
@ -17,6 +21,10 @@ LL | a == b;
|
||||||
| ^^ no implementation for `&T == T`
|
| ^^ no implementation for `&T == T`
|
||||||
|
|
|
|
||||||
= help: the trait `PartialEq<T>` is not implemented for `&T`
|
= help: the trait `PartialEq<T>` 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
|
help: consider extending the `where` clause, but there might be an alternative better way to express this requirement
|
||||||
|
|
|
|
||||||
LL | fn foo2<T: PartialEq>(a: &T, b: T) where &T: PartialEq<T> {
|
LL | fn foo2<T: PartialEq>(a: &T, b: T) where &T: PartialEq<T> {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue