mirror of
https://github.com/rust-lang/rust.git
synced 2025-05-14 02:49:40 +00:00
Tweak binop errors
* Suggest potentially missing binop trait bound (fix #73416) * Use structured suggestion for dereference in binop
This commit is contained in:
parent
a39c7787ba
commit
5aab1a9a88
@ -9,7 +9,9 @@ use rustc_middle::ty::adjustment::{
|
|||||||
Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability,
|
Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability,
|
||||||
};
|
};
|
||||||
use rustc_middle::ty::TyKind::{Adt, Array, Char, FnDef, Never, Ref, Str, Tuple, Uint};
|
use rustc_middle::ty::TyKind::{Adt, Array, Char, FnDef, Never, Ref, Str, Tuple, Uint};
|
||||||
use rustc_middle::ty::{self, suggest_constraining_type_param, Ty, TyCtxt, TypeFoldable};
|
use rustc_middle::ty::{
|
||||||
|
self, suggest_constraining_type_param, Ty, TyCtxt, TypeFoldable, TypeVisitor,
|
||||||
|
};
|
||||||
use rustc_span::symbol::Ident;
|
use rustc_span::symbol::Ident;
|
||||||
use rustc_span::Span;
|
use rustc_span::Span;
|
||||||
use rustc_trait_selection::infer::InferCtxtExt;
|
use rustc_trait_selection::infer::InferCtxtExt;
|
||||||
@ -254,6 +256,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||||||
if !lhs_ty.references_error() && !rhs_ty.references_error() {
|
if !lhs_ty.references_error() && !rhs_ty.references_error() {
|
||||||
let source_map = self.tcx.sess.source_map();
|
let source_map = self.tcx.sess.source_map();
|
||||||
|
|
||||||
|
let note = |err: &mut DiagnosticBuilder<'_>, missing_trait| {
|
||||||
|
err.note(&format!(
|
||||||
|
"the trait `{}` is not implemented for `{}`",
|
||||||
|
missing_trait, lhs_ty
|
||||||
|
));
|
||||||
|
};
|
||||||
match is_assign {
|
match is_assign {
|
||||||
IsAssign::Yes => {
|
IsAssign::Yes => {
|
||||||
let mut err = struct_span_err!(
|
let mut err = struct_span_err!(
|
||||||
@ -286,10 +294,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||||||
rty.peel_refs(),
|
rty.peel_refs(),
|
||||||
lstring,
|
lstring,
|
||||||
);
|
);
|
||||||
err.span_suggestion(
|
err.span_suggestion_verbose(
|
||||||
lhs_expr.span,
|
lhs_expr.span.shrink_to_lo(),
|
||||||
msg,
|
msg,
|
||||||
format!("*{}", lstring),
|
"*".to_string(),
|
||||||
rustc_errors::Applicability::MachineApplicable,
|
rustc_errors::Applicability::MachineApplicable,
|
||||||
);
|
);
|
||||||
suggested_deref = true;
|
suggested_deref = true;
|
||||||
@ -310,6 +318,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
if let Some(missing_trait) = missing_trait {
|
if let Some(missing_trait) = missing_trait {
|
||||||
|
let mut visitor = TypeParamVisitor(vec![]);
|
||||||
|
visitor.visit_ty(lhs_ty);
|
||||||
|
|
||||||
|
let mut sugg = false;
|
||||||
if op.node == hir::BinOpKind::Add
|
if op.node == hir::BinOpKind::Add
|
||||||
&& self.check_str_addition(
|
&& self.check_str_addition(
|
||||||
lhs_expr, rhs_expr, lhs_ty, rhs_ty, &mut err, true, op,
|
lhs_expr, rhs_expr, lhs_ty, rhs_ty, &mut err, true, op,
|
||||||
@ -318,18 +330,33 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||||||
// This has nothing here because it means we did string
|
// This has nothing here because it means we did string
|
||||||
// concatenation (e.g., "Hello " += "World!"). This means
|
// concatenation (e.g., "Hello " += "World!"). This means
|
||||||
// we don't want the note in the else clause to be emitted
|
// we don't want the note in the else clause to be emitted
|
||||||
} else if let ty::Param(p) = lhs_ty.kind {
|
sugg = true;
|
||||||
suggest_constraining_param(
|
} else if let [ty] = &visitor.0[..] {
|
||||||
self.tcx,
|
if let ty::Param(p) = ty.kind {
|
||||||
self.body_id,
|
// FIXME: This *guesses* that constraining the type param
|
||||||
&mut err,
|
// will make the operation available, but this is only true
|
||||||
lhs_ty,
|
// when the corresponding trait has a blanked
|
||||||
rhs_ty,
|
// implementation, like the following:
|
||||||
missing_trait,
|
// `impl<'a> PartialEq for &'a [T] where T: PartialEq {}`
|
||||||
p,
|
// The correct thing to do would be to verify this
|
||||||
false,
|
// projection would hold.
|
||||||
);
|
if *ty != lhs_ty {
|
||||||
} else if !suggested_deref {
|
note(&mut err, missing_trait);
|
||||||
|
}
|
||||||
|
suggest_constraining_param(
|
||||||
|
self.tcx,
|
||||||
|
self.body_id,
|
||||||
|
&mut err,
|
||||||
|
ty,
|
||||||
|
rhs_ty,
|
||||||
|
missing_trait,
|
||||||
|
p,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
sugg = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !sugg && !suggested_deref {
|
||||||
suggest_impl_missing(&mut err, lhs_ty, &missing_trait);
|
suggest_impl_missing(&mut err, lhs_ty, &missing_trait);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -458,18 +485,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||||||
.is_ok()
|
.is_ok()
|
||||||
} {
|
} {
|
||||||
if let Ok(lstring) = source_map.span_to_snippet(lhs_expr.span) {
|
if let Ok(lstring) = source_map.span_to_snippet(lhs_expr.span) {
|
||||||
err.help(&format!(
|
err.span_suggestion_verbose(
|
||||||
"`{}` can be used on '{}', you can \
|
lhs_expr.span.shrink_to_lo(),
|
||||||
dereference `{2}`: `*{2}`",
|
&format!(
|
||||||
op.node.as_str(),
|
"`{}` can be used on `{}`, you can dereference \
|
||||||
rty.peel_refs(),
|
`{}`",
|
||||||
lstring
|
op.node.as_str(),
|
||||||
));
|
rty.peel_refs(),
|
||||||
|
lstring,
|
||||||
|
),
|
||||||
|
"*".to_string(),
|
||||||
|
Applicability::MachineApplicable,
|
||||||
|
);
|
||||||
suggested_deref = true;
|
suggested_deref = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(missing_trait) = missing_trait {
|
if let Some(missing_trait) = missing_trait {
|
||||||
|
let mut visitor = TypeParamVisitor(vec![]);
|
||||||
|
visitor.visit_ty(lhs_ty);
|
||||||
|
|
||||||
|
let mut sugg = false;
|
||||||
if op.node == hir::BinOpKind::Add
|
if op.node == hir::BinOpKind::Add
|
||||||
&& self.check_str_addition(
|
&& self.check_str_addition(
|
||||||
lhs_expr, rhs_expr, lhs_ty, rhs_ty, &mut err, false, op,
|
lhs_expr, rhs_expr, lhs_ty, rhs_ty, &mut err, false, op,
|
||||||
@ -478,18 +514,33 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||||||
// This has nothing here because it means we did string
|
// This has nothing here because it means we did string
|
||||||
// concatenation (e.g., "Hello " + "World!"). This means
|
// concatenation (e.g., "Hello " + "World!"). This means
|
||||||
// we don't want the note in the else clause to be emitted
|
// we don't want the note in the else clause to be emitted
|
||||||
} else if let ty::Param(p) = lhs_ty.kind {
|
sugg = true;
|
||||||
suggest_constraining_param(
|
} else if let [ty] = &visitor.0[..] {
|
||||||
self.tcx,
|
if let ty::Param(p) = ty.kind {
|
||||||
self.body_id,
|
// FIXME: This *guesses* that constraining the type param
|
||||||
&mut err,
|
// will make the operation available, but this is only true
|
||||||
lhs_ty,
|
// when the corresponding trait has a blanked
|
||||||
rhs_ty,
|
// implementation, like the following:
|
||||||
missing_trait,
|
// `impl<'a> PartialEq for &'a [T] where T: PartialEq {}`
|
||||||
p,
|
// The correct thing to do would be to verify this
|
||||||
use_output,
|
// projection would hold.
|
||||||
);
|
if *ty != lhs_ty {
|
||||||
} else if !suggested_deref && !involves_fn {
|
note(&mut err, missing_trait);
|
||||||
|
}
|
||||||
|
suggest_constraining_param(
|
||||||
|
self.tcx,
|
||||||
|
self.body_id,
|
||||||
|
&mut err,
|
||||||
|
ty,
|
||||||
|
rhs_ty,
|
||||||
|
missing_trait,
|
||||||
|
p,
|
||||||
|
use_output,
|
||||||
|
);
|
||||||
|
sugg = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !sugg && !suggested_deref && !involves_fn {
|
||||||
suggest_impl_missing(&mut err, lhs_ty, &missing_trait);
|
suggest_impl_missing(&mut err, lhs_ty, &missing_trait);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -928,8 +979,7 @@ fn suggest_impl_missing(err: &mut DiagnosticBuilder<'_>, ty: Ty<'_>, missing_tra
|
|||||||
if let Adt(def, _) = ty.peel_refs().kind {
|
if let Adt(def, _) = ty.peel_refs().kind {
|
||||||
if def.did.is_local() {
|
if def.did.is_local() {
|
||||||
err.note(&format!(
|
err.note(&format!(
|
||||||
"an implementation of `{}` might \
|
"an implementation of `{}` might be missing for `{}`",
|
||||||
be missing for `{}`",
|
|
||||||
missing_trait, ty
|
missing_trait, ty
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@ -975,3 +1025,14 @@ fn suggest_constraining_param(
|
|||||||
err.span_label(span, msg);
|
err.span_label(span, msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct TypeParamVisitor<'tcx>(Vec<Ty<'tcx>>);
|
||||||
|
|
||||||
|
impl<'tcx> TypeVisitor<'tcx> for TypeParamVisitor<'tcx> {
|
||||||
|
fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool {
|
||||||
|
if let ty::Param(_) = ty.kind {
|
||||||
|
self.0.push(ty);
|
||||||
|
}
|
||||||
|
ty.super_visit_with(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
9
src/test/ui/binary-op-on-double-ref.fixed
Normal file
9
src/test/ui/binary-op-on-double-ref.fixed
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
// run-rustfix
|
||||||
|
fn main() {
|
||||||
|
let v = vec![1, 2, 3, 4, 5, 6, 7, 8, 9];
|
||||||
|
let vr = v.iter().filter(|x| {
|
||||||
|
*x % 2 == 0
|
||||||
|
//~^ ERROR cannot mod `&&{integer}` by `{integer}`
|
||||||
|
});
|
||||||
|
println!("{:?}", vr);
|
||||||
|
}
|
@ -1,3 +1,4 @@
|
|||||||
|
// run-rustfix
|
||||||
fn main() {
|
fn main() {
|
||||||
let v = vec![1, 2, 3, 4, 5, 6, 7, 8, 9];
|
let v = vec![1, 2, 3, 4, 5, 6, 7, 8, 9];
|
||||||
let vr = v.iter().filter(|x| {
|
let vr = v.iter().filter(|x| {
|
||||||
|
@ -1,12 +1,15 @@
|
|||||||
error[E0369]: cannot mod `&&{integer}` by `{integer}`
|
error[E0369]: cannot mod `&&{integer}` by `{integer}`
|
||||||
--> $DIR/binary-op-on-double-ref.rs:4:11
|
--> $DIR/binary-op-on-double-ref.rs:5:11
|
||||||
|
|
|
|
||||||
LL | x % 2 == 0
|
LL | x % 2 == 0
|
||||||
| - ^ - {integer}
|
| - ^ - {integer}
|
||||||
| |
|
| |
|
||||||
| &&{integer}
|
| &&{integer}
|
||||||
|
|
|
|
||||||
= help: `%` can be used on '{integer}', you can dereference `x`: `*x`
|
help: `%` can be used on `{integer}`, you can dereference `x`
|
||||||
|
|
|
||||||
|
LL | *x % 2 == 0
|
||||||
|
| ^
|
||||||
|
|
||||||
error: aborting due to previous error
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
@ -5,6 +5,12 @@ LL | a.iter().map(|a| a*a)
|
|||||||
| -^- &T
|
| -^- &T
|
||||||
| |
|
| |
|
||||||
| &T
|
| &T
|
||||||
|
|
|
||||||
|
= note: the trait `std::ops::Mul` is not implemented for `&T`
|
||||||
|
help: consider restricting type parameter `T`
|
||||||
|
|
|
||||||
|
LL | fn func<'a, T: std::ops::Mul<Output = &T>>(a: &'a [T]) -> impl Iterator<Item=&'a T> {
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
error: aborting due to previous error
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ LL | let x = |ref x: isize| { x += 1; };
|
|||||||
help: `+=` can be used on 'isize', you can dereference `x`
|
help: `+=` can be used on 'isize', you can dereference `x`
|
||||||
|
|
|
|
||||||
LL | let x = |ref x: isize| { *x += 1; };
|
LL | let x = |ref x: isize| { *x += 1; };
|
||||||
| ^^
|
| ^
|
||||||
|
|
||||||
error: aborting due to previous error
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
13
src/test/ui/suggestions/missing-trait-bound-for-op.fixed
Normal file
13
src/test/ui/suggestions/missing-trait-bound-for-op.fixed
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
// run-rustfix
|
||||||
|
|
||||||
|
pub fn strip_prefix<'a, T: std::cmp::PartialEq>(s: &'a [T], prefix: &[T]) -> Option<&'a [T]> {
|
||||||
|
let n = prefix.len();
|
||||||
|
if n <= s.len() {
|
||||||
|
let (head, tail) = s.split_at(n);
|
||||||
|
if head == prefix { //~ ERROR binary operation `==` cannot be applied to type `&[T]`
|
||||||
|
return Some(tail);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
fn main() {}
|
13
src/test/ui/suggestions/missing-trait-bound-for-op.rs
Normal file
13
src/test/ui/suggestions/missing-trait-bound-for-op.rs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
// run-rustfix
|
||||||
|
|
||||||
|
pub fn strip_prefix<'a, T>(s: &'a [T], prefix: &[T]) -> Option<&'a [T]> {
|
||||||
|
let n = prefix.len();
|
||||||
|
if n <= s.len() {
|
||||||
|
let (head, tail) = s.split_at(n);
|
||||||
|
if head == prefix { //~ ERROR binary operation `==` cannot be applied to type `&[T]`
|
||||||
|
return Some(tail);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
fn main() {}
|
17
src/test/ui/suggestions/missing-trait-bound-for-op.stderr
Normal file
17
src/test/ui/suggestions/missing-trait-bound-for-op.stderr
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
error[E0369]: binary operation `==` cannot be applied to type `&[T]`
|
||||||
|
--> $DIR/missing-trait-bound-for-op.rs:7:17
|
||||||
|
|
|
||||||
|
LL | if head == prefix {
|
||||||
|
| ---- ^^ ------ &[T]
|
||||||
|
| |
|
||||||
|
| &[T]
|
||||||
|
|
|
||||||
|
= note: the trait `std::cmp::PartialEq` is not implemented for `&[T]`
|
||||||
|
help: consider restricting type parameter `T`
|
||||||
|
|
|
||||||
|
LL | pub fn strip_prefix<'a, T: std::cmp::PartialEq>(s: &'a [T], prefix: &[T]) -> Option<&'a [T]> {
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0369`.
|
@ -5,6 +5,12 @@ LL | a * b
|
|||||||
| - ^ - f64
|
| - ^ - f64
|
||||||
| |
|
| |
|
||||||
| &T
|
| &T
|
||||||
|
|
|
||||||
|
= note: the trait `std::ops::Mul` is not implemented for `&T`
|
||||||
|
help: consider further restricting this bound
|
||||||
|
|
|
||||||
|
LL | fn foo<T: MyMul<f64, f64> + std::ops::Mul<Output = f64>>(a: &T, b: f64) -> f64 {
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
error: aborting due to previous error
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user