Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability,
};
use rustc_middle::ty::TyKind::{Adt, Array, Char, FnDef, Never, Ref, Str, Tuple, Uint};
-use rustc_middle::ty::{self, Ty, TypeFoldable};
+use rustc_middle::ty::{self, suggest_constraining_type_param, Ty, TyCtxt, TypeFoldable};
use rustc_span::Span;
use rustc_trait_selection::infer::InferCtxtExt;
// error types are considered "builtin"
if !lhs_ty.references_error() && !rhs_ty.references_error() {
let source_map = self.tcx.sess.source_map();
+
match is_assign {
IsAssign::Yes => {
let mut err = struct_span_err!(
// This has nothing here because it means we did string
// concatenation (e.g., "Hello " += "World!"). This means
// we don't want the note in the else clause to be emitted
- } else if let ty::Param(_) = lhs_ty.kind {
- // FIXME: point to span of param
- err.note(&format!(
- "`{}` might need a bound for `{}`",
- lhs_ty, missing_trait
- ));
+ } else if let ty::Param(p) = lhs_ty.kind {
+ suggest_constraining_param(
+ self.tcx,
+ self.body_id,
+ &mut err,
+ lhs_ty,
+ rhs_ty,
+ missing_trait,
+ p,
+ false,
+ );
} else if !suggested_deref {
suggest_impl_missing(&mut err, lhs_ty, &missing_trait);
}
err.emit();
}
IsAssign::No => {
- let (message, missing_trait) = match op.node {
+ let (message, missing_trait, use_output) = match op.node {
hir::BinOpKind::Add => (
format!("cannot add `{}` to `{}`", rhs_ty, lhs_ty),
Some("std::ops::Add"),
+ true,
),
hir::BinOpKind::Sub => (
format!("cannot subtract `{}` from `{}`", rhs_ty, lhs_ty),
Some("std::ops::Sub"),
+ true,
),
hir::BinOpKind::Mul => (
format!("cannot multiply `{}` to `{}`", rhs_ty, lhs_ty),
Some("std::ops::Mul"),
+ true,
),
hir::BinOpKind::Div => (
format!("cannot divide `{}` by `{}`", lhs_ty, rhs_ty),
Some("std::ops::Div"),
+ true,
),
hir::BinOpKind::Rem => (
format!("cannot mod `{}` by `{}`", lhs_ty, rhs_ty),
Some("std::ops::Rem"),
+ true,
),
hir::BinOpKind::BitAnd => (
format!("no implementation for `{} & {}`", lhs_ty, rhs_ty),
Some("std::ops::BitAnd"),
+ true,
),
hir::BinOpKind::BitXor => (
format!("no implementation for `{} ^ {}`", lhs_ty, rhs_ty),
Some("std::ops::BitXor"),
+ true,
),
hir::BinOpKind::BitOr => (
format!("no implementation for `{} | {}`", lhs_ty, rhs_ty),
Some("std::ops::BitOr"),
+ true,
),
hir::BinOpKind::Shl => (
format!("no implementation for `{} << {}`", lhs_ty, rhs_ty),
Some("std::ops::Shl"),
+ true,
),
hir::BinOpKind::Shr => (
format!("no implementation for `{} >> {}`", lhs_ty, rhs_ty),
Some("std::ops::Shr"),
+ true,
),
hir::BinOpKind::Eq | hir::BinOpKind::Ne => (
format!(
lhs_ty
),
Some("std::cmp::PartialEq"),
+ false,
),
hir::BinOpKind::Lt
| hir::BinOpKind::Le
lhs_ty
),
Some("std::cmp::PartialOrd"),
+ false,
),
_ => (
format!(
lhs_ty
),
None,
+ false,
),
};
let mut err = struct_span_err!(
// This has nothing here because it means we did string
// concatenation (e.g., "Hello " + "World!"). This means
// we don't want the note in the else clause to be emitted
- } else if let ty::Param(_) = lhs_ty.kind {
- // FIXME: point to span of param
- err.note(&format!(
- "`{}` might need a bound for `{}`",
- lhs_ty, missing_trait
- ));
+ } else if let ty::Param(p) = lhs_ty.kind {
+ suggest_constraining_param(
+ self.tcx,
+ self.body_id,
+ &mut err,
+ lhs_ty,
+ rhs_ty,
+ missing_trait,
+ p,
+ use_output,
+ );
} else if !suggested_deref && !involves_fn {
suggest_impl_missing(&mut err, lhs_ty, &missing_trait);
}
}
}
}
+
+fn suggest_constraining_param(
+ tcx: TyCtxt<'_>,
+ body_id: hir::HirId,
+ mut err: &mut DiagnosticBuilder<'_>,
+ lhs_ty: Ty<'_>,
+ rhs_ty: Ty<'_>,
+ missing_trait: &str,
+ p: ty::ParamTy,
+ set_output: bool,
+) {
+ let hir = tcx.hir();
+ let msg = &format!("`{}` might need a bound for `{}`", lhs_ty, missing_trait);
+ // Try to find the def-id and details for the parameter p. We have only the index,
+ // so we have to find the enclosing function's def-id, then look through its declared
+ // generic parameters to get the declaration.
+ let def_id = hir.body_owner_def_id(hir::BodyId { hir_id: body_id });
+ let generics = tcx.generics_of(def_id);
+ let param_def_id = generics.type_param(&p, tcx).def_id;
+ if let Some(generics) = param_def_id
+ .as_local()
+ .map(|id| hir.as_local_hir_id(id))
+ .and_then(|id| hir.find(hir.get_parent_item(id)))
+ .as_ref()
+ .and_then(|node| node.generics())
+ {
+ let output = if set_output { format!("<Output = {}>", rhs_ty) } else { String::new() };
+ suggest_constraining_type_param(
+ tcx,
+ generics,
+ &mut err,
+ &format!("{}", lhs_ty),
+ &format!("{}{}", missing_trait, output),
+ None,
+ );
+ } else {
+ let span = tcx.def_span(param_def_id);
+ err.span_label(span, msg);
+ }
+}