]> git.lizzy.rs Git - rust.git/blobdiff - src/librustc_typeck/check/op.rs
Rollup merge of #70908 - estebank:suggest-add, r=nikomatsakis
[rust.git] / src / librustc_typeck / check / op.rs
index 23004cf364725d349e76f9b85e6a45cde516af6d..e6cbc8ab7230c79795c8180c72730fcba3019329 100644 (file)
@@ -10,7 +10,7 @@
     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;
 
@@ -253,6 +253,7 @@ fn check_overloaded_binop(
                 // 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!(
@@ -317,12 +318,17 @@ fn check_overloaded_binop(
                                     // 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);
                                 }
@@ -330,46 +336,56 @@ fn check_overloaded_binop(
                             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!(
@@ -378,6 +394,7 @@ fn check_overloaded_binop(
                                         lhs_ty
                                     ),
                                     Some("std::cmp::PartialEq"),
+                                    false,
                                 ),
                                 hir::BinOpKind::Lt
                                 | hir::BinOpKind::Le
@@ -389,6 +406,7 @@ fn check_overloaded_binop(
                                         lhs_ty
                                     ),
                                     Some("std::cmp::PartialOrd"),
+                                    false,
                                 ),
                                 _ => (
                                     format!(
@@ -397,6 +415,7 @@ fn check_overloaded_binop(
                                         lhs_ty
                                     ),
                                     None,
+                                    false,
                                 ),
                             };
                             let mut err = struct_span_err!(
@@ -459,12 +478,17 @@ fn check_overloaded_binop(
                                     // 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);
                                 }
@@ -911,3 +935,43 @@ fn suggest_impl_missing(err: &mut DiagnosticBuilder<'_>, ty: Ty<'_>, missing_tra
         }
     }
 }
+
+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);
+    }
+}