about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustc_typeck/check/op.rs235
-rw-r--r--src/test/ui/binary-op-on-double-ref.stderr3
-rw-r--r--src/test/ui/codemap_tests/issue-28308.stderr2
-rw-r--r--src/test/ui/error-codes/E0067.stderr2
-rw-r--r--src/test/ui/error-codes/E0600.stderr2
-rw-r--r--src/test/ui/error-festival.stderr6
-rw-r--r--src/test/ui/feature-gate-negate-unsigned.stderr8
-rw-r--r--src/test/ui/issue-5239-1.stderr2
-rw-r--r--src/test/ui/reachable/expr_unary.stderr2
-rw-r--r--src/test/ui/type-check/missing_trait_impl.rs4
-rw-r--r--src/test/ui/type-check/missing_trait_impl.stderr15
11 files changed, 201 insertions, 80 deletions
diff --git a/src/librustc_typeck/check/op.rs b/src/librustc_typeck/check/op.rs
index 9e8e00594e6..23081b87d26 100644
--- a/src/librustc_typeck/check/op.rs
+++ b/src/librustc_typeck/check/op.rs
@@ -12,8 +12,8 @@
 
 use super::{FnCtxt, Needs};
 use super::method::MethodCallee;
-use rustc::ty::{self, Ty, TypeFoldable, TypeVariants};
-use rustc::ty::TypeVariants::{TyStr, TyRef, TyAdt};
+use rustc::ty::{self, Ty, TypeFoldable};
+use rustc::ty::TypeVariants::{TyRef, TyAdt, TyStr, TyUint, TyNever, TyTuple, TyChar, TyArray};
 use rustc::ty::adjustment::{Adjustment, Adjust, AllowTwoPhase, AutoBorrow, AutoBorrowMutability};
 use rustc::infer::type_variable::TypeVariableOrigin;
 use errors;
@@ -246,76 +246,151 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
             Err(()) => {
                 // error types are considered "builtin"
                 if !lhs_ty.references_error() {
-                    if let IsAssign::Yes = is_assign {
-                        struct_span_err!(self.tcx.sess, expr.span, E0368,
-                                         "binary assignment operation `{}=` \
-                                          cannot be applied to type `{}`",
-                                         op.node.as_str(),
-                                         lhs_ty)
-                            .span_label(lhs_expr.span,
-                                        format!("cannot use `{}=` on type `{}`",
-                                        op.node.as_str(), lhs_ty))
-                            .emit();
-                    } else {
-                        let mut err = struct_span_err!(self.tcx.sess, expr.span, E0369,
-                            "binary operation `{}` cannot be applied to type `{}`",
-                            op.node.as_str(),
-                            lhs_ty);
-
-                        if let TypeVariants::TyRef(_, rty, _) = lhs_ty.sty {
-                            if {
-                                !self.infcx.type_moves_by_default(self.param_env,
-                                                                  rty,
-                                                                  lhs_expr.span) &&
-                                    self.lookup_op_method(rty,
-                                                          &[rhs_ty],
-                                                          Op::Binary(op, is_assign))
-                                        .is_ok()
-                            } {
-                                err.note(
-                                    &format!(
-                                        "this is a reference to a type that `{}` can be applied \
-                                        to; you need to dereference this variable once for this \
-                                        operation to work",
-                                    op.node.as_str()));
+                    let codemap = self.tcx.sess.codemap();
+                    match is_assign {
+                        IsAssign::Yes => {
+                            let mut err = struct_span_err!(self.tcx.sess, expr.span, E0368,
+                                                "binary assignment operation `{}=` \
+                                                cannot be applied to type `{}`",
+                                                op.node.as_str(),
+                                                lhs_ty);
+                            err.span_label(lhs_expr.span,
+                                    format!("cannot use `{}=` on type `{}`",
+                                    op.node.as_str(), lhs_ty));
+                            let mut suggested_deref = false;
+                            if let TyRef(_, mut rty, _) = lhs_ty.sty {
+                                if {
+                                    !self.infcx.type_moves_by_default(self.param_env,
+                                                                        rty,
+                                                                        lhs_expr.span) &&
+                                        self.lookup_op_method(rty,
+                                                              &[rhs_ty],
+                                                              Op::Binary(op, is_assign))
+                                            .is_ok()
+                                } {
+                                    if let Ok(lstring) = codemap.span_to_snippet(lhs_expr.span) {
+                                        while let TyRef(_, rty_inner, _) = rty.sty {
+                                            rty = rty_inner;
+                                        }
+                                        let msg = &format!(
+                                                "`{}=` can be used on '{}', you can \
+                                                dereference `{2}`: `*{2}`",
+                                                op.node.as_str(),
+                                                rty,
+                                                lstring
+                                        );
+                                        err.help(msg);
+                                        suggested_deref = true;
+                                    }
+                                }
                             }
+                            let missing_trait = match op.node {
+                                hir::BiAdd    => Some("std::ops::AddAssign"),
+                                hir::BiSub    => Some("std::ops::SubAssign"),
+                                hir::BiMul    => Some("std::ops::MulAssign"),
+                                hir::BiDiv    => Some("std::ops::DivAssign"),
+                                hir::BiRem    => Some("std::ops::RemAssign"),
+                                hir::BiBitAnd => Some("std::ops::BitAndAssign"),
+                                hir::BiBitXor => Some("std::ops::BitXorAssign"),
+                                hir::BiBitOr  => Some("std::ops::BitOrAssign"),
+                                hir::BiShl    => Some("std::ops::ShlAssign"),
+                                hir::BiShr    => Some("std::ops::ShrAssign"),
+                                _             => None
+                            };
+                            if let Some(missing_trait) = missing_trait {
+                                if op.node == hir::BiAdd &&
+                                    self.check_str_addition(expr, lhs_expr, rhs_expr, lhs_ty,
+                                                            rhs_ty, &mut 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::TyParam(_) = lhs_ty.sty {
+                                    // FIXME: point to span of param
+                                    err.note(&format!(
+                                        "`{}` might need a bound for `{}`",
+                                        lhs_ty, missing_trait
+                                    ));
+                                } else if !suggested_deref {
+                                    err.note(&format!(
+                                        "an implementation of `{}` might \
+                                         be missing for `{}`",
+                                        missing_trait, lhs_ty
+                                    ));
+                                }
+                            }
+                            err.emit();
                         }
-
-                        let missing_trait = match op.node {
-                            hir::BiAdd    => Some("std::ops::Add"),
-                            hir::BiSub    => Some("std::ops::Sub"),
-                            hir::BiMul    => Some("std::ops::Mul"),
-                            hir::BiDiv    => Some("std::ops::Div"),
-                            hir::BiRem    => Some("std::ops::Rem"),
-                            hir::BiBitAnd => Some("std::ops::BitAnd"),
-                            hir::BiBitOr  => Some("std::ops::BitOr"),
-                            hir::BiShl    => Some("std::ops::Shl"),
-                            hir::BiShr    => Some("std::ops::Shr"),
-                            hir::BiEq | hir::BiNe => Some("std::cmp::PartialEq"),
-                            hir::BiLt | hir::BiLe | hir::BiGt | hir::BiGe =>
-                                Some("std::cmp::PartialOrd"),
-                            _             => None
-                        };
-
-                        if let Some(missing_trait) = missing_trait {
-                            if missing_trait == "std::ops::Add" &&
-                                self.check_str_addition(expr, lhs_expr, rhs_expr, lhs_ty,
-                                                        rhs_ty, &mut 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::TyParam(_) = lhs_ty.sty {
-                                // FIXME: point to span of param
-                                err.note(
-                                    &format!("`{}` might need a bound for `{}`",
-                                             lhs_ty, missing_trait));
-                            } else {
-                                err.note(
-                                    &format!("an implementation of `{}` might be missing for `{}`",
-                                             missing_trait, lhs_ty));
+                        IsAssign::No => {
+                            let mut err = struct_span_err!(self.tcx.sess, expr.span, E0369,
+                                            "binary operation `{}` cannot be applied to type `{}`",
+                                            op.node.as_str(),
+                                            lhs_ty);
+                            let mut suggested_deref = false;
+                            if let TyRef(_, mut rty, _) = lhs_ty.sty {
+                                if {
+                                    !self.infcx.type_moves_by_default(self.param_env,
+                                                                        rty,
+                                                                        lhs_expr.span) &&
+                                        self.lookup_op_method(rty,
+                                                              &[rhs_ty],
+                                                              Op::Binary(op, is_assign))
+                                            .is_ok()
+                                } {
+                                    if let Ok(lstring) = codemap.span_to_snippet(lhs_expr.span) {
+                                        while let TyRef(_, rty_inner, _) = rty.sty {
+                                            rty = rty_inner;
+                                        }
+                                        let msg = &format!(
+                                                "`{}` can be used on '{}', you can \
+                                                dereference `{2}`: `*{2}`",
+                                                op.node.as_str(),
+                                                rty,
+                                                lstring
+                                        );
+                                        err.help(msg);
+                                        suggested_deref = true;
+                                    }
+                                }
                             }
+                            let missing_trait = match op.node {
+                                hir::BiAdd    => Some("std::ops::Add"),
+                                hir::BiSub    => Some("std::ops::Sub"),
+                                hir::BiMul    => Some("std::ops::Mul"),
+                                hir::BiDiv    => Some("std::ops::Div"),
+                                hir::BiRem    => Some("std::ops::Rem"),
+                                hir::BiBitAnd => Some("std::ops::BitAnd"),
+                                hir::BiBitXor => Some("std::ops::BitXor"),
+                                hir::BiBitOr  => Some("std::ops::BitOr"),
+                                hir::BiShl    => Some("std::ops::Shl"),
+                                hir::BiShr    => Some("std::ops::Shr"),
+                                hir::BiEq | hir::BiNe => Some("std::cmp::PartialEq"),
+                                hir::BiLt | hir::BiLe | hir::BiGt | hir::BiGe =>
+                                    Some("std::cmp::PartialOrd"),
+                                _             => None
+                            };
+                            if let Some(missing_trait) = missing_trait {
+                                if op.node == hir::BiAdd &&
+                                    self.check_str_addition(expr, lhs_expr, rhs_expr, lhs_ty,
+                                                            rhs_ty, &mut 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::TyParam(_) = lhs_ty.sty {
+                                    // FIXME: point to span of param
+                                    err.note(&format!(
+                                        "`{}` might need a bound for `{}`",
+                                        lhs_ty, missing_trait
+                                    ));
+                                } else if !suggested_deref {
+                                    err.note(&format!(
+                                        "an implementation of `{}` might \
+                                         be missing for `{}`",
+                                        missing_trait, lhs_ty
+                                    ));
+                                }
+                            }
+                            err.emit();
                         }
-                        err.emit();
                     }
                 }
                 self.tcx.types.err
@@ -393,9 +468,29 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
             Err(()) => {
                 let actual = self.resolve_type_vars_if_possible(&operand_ty);
                 if !actual.references_error() {
-                    struct_span_err!(self.tcx.sess, ex.span, E0600,
+                    let mut err = struct_span_err!(self.tcx.sess, ex.span, E0600,
                                      "cannot apply unary operator `{}` to type `{}`",
-                                     op.as_str(), actual).emit();
+                                     op.as_str(), actual);
+                    err.span_label(ex.span, format!("cannot apply unary \
+                                                    operator `{}`", op.as_str()));
+                    match actual.sty {
+                        TyUint(_) if op == hir::UnNeg => {
+                            err.note(&format!("unsigned values cannot be negated"));
+                        },
+                        TyStr | TyNever | TyChar | TyTuple(_) | TyArray(_,_) => {},
+                        TyRef(_, ref lty, _) if lty.sty == TyStr => {},
+                        _ => {
+                            let missing_trait = match op {
+                                hir::UnNeg => "std::ops::Neg",
+                                hir::UnNot => "std::ops::Not",
+                                hir::UnDeref => "std::ops::UnDerf"
+                            };
+                            err.note(&format!("an implementation of `{}` might \
+                                                be missing for `{}`",
+                                             missing_trait, operand_ty));
+                        }
+                    }
+                    err.emit();
                 }
                 self.tcx.types.err
             }
diff --git a/src/test/ui/binary-op-on-double-ref.stderr b/src/test/ui/binary-op-on-double-ref.stderr
index 07aa3bfe40d..c89defa3dd1 100644
--- a/src/test/ui/binary-op-on-double-ref.stderr
+++ b/src/test/ui/binary-op-on-double-ref.stderr
@@ -4,8 +4,7 @@ error[E0369]: binary operation `%` cannot be applied to type `&&{integer}`
 LL |         x % 2 == 0
    |         ^^^^^
    |
-   = note: this is a reference to a type that `%` can be applied to; you need to dereference this variable once for this operation to work
-   = note: an implementation of `std::ops::Rem` might be missing for `&&{integer}`
+   = help: `%` can be used on '{integer}', you can dereference `x`: `*x`
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/codemap_tests/issue-28308.stderr b/src/test/ui/codemap_tests/issue-28308.stderr
index 2c8a33d95c0..15c159a3b15 100644
--- a/src/test/ui/codemap_tests/issue-28308.stderr
+++ b/src/test/ui/codemap_tests/issue-28308.stderr
@@ -2,7 +2,7 @@ error[E0600]: cannot apply unary operator `!` to type `&'static str`
   --> $DIR/issue-28308.rs:12:5
    |
 LL |     assert!("foo");
-   |     ^^^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^^^ cannot apply unary operator `!`
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/error-codes/E0067.stderr b/src/test/ui/error-codes/E0067.stderr
index 76f06c7c463..43e1ca4096c 100644
--- a/src/test/ui/error-codes/E0067.stderr
+++ b/src/test/ui/error-codes/E0067.stderr
@@ -5,6 +5,8 @@ LL |     LinkedList::new() += 1; //~ ERROR E0368
    |     -----------------^^^^^
    |     |
    |     cannot use `+=` on type `std::collections::LinkedList<_>`
+   |
+   = note: an implementation of `std::ops::AddAssign` might be missing for `std::collections::LinkedList<_>`
 
 error[E0067]: invalid left-hand side expression
   --> $DIR/E0067.rs:14:5
diff --git a/src/test/ui/error-codes/E0600.stderr b/src/test/ui/error-codes/E0600.stderr
index 500feb39f5e..c29ec4fe6ae 100644
--- a/src/test/ui/error-codes/E0600.stderr
+++ b/src/test/ui/error-codes/E0600.stderr
@@ -2,7 +2,7 @@ error[E0600]: cannot apply unary operator `!` to type `&'static str`
   --> $DIR/E0600.rs:12:5
    |
 LL |     !"a"; //~ ERROR E0600
-   |     ^^^^
+   |     ^^^^ cannot apply unary operator `!`
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/error-festival.stderr b/src/test/ui/error-festival.stderr
index 345691352b4..69f11b4b7c0 100644
--- a/src/test/ui/error-festival.stderr
+++ b/src/test/ui/error-festival.stderr
@@ -17,6 +17,8 @@ LL |     x += 2;
    |     -^^^^^
    |     |
    |     cannot use `+=` on type `&str`
+   |
+   = note: an implementation of `std::ops::AddAssign` might be missing for `&str`
 
 error[E0599]: no method named `z` found for type `&str` in the current scope
   --> $DIR/error-festival.rs:26:7
@@ -28,7 +30,9 @@ error[E0600]: cannot apply unary operator `!` to type `Question`
   --> $DIR/error-festival.rs:29:5
    |
 LL |     !Question::Yes;
-   |     ^^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^^ cannot apply unary operator `!`
+   |
+   = note: an implementation of `std::ops::Not` might be missing for `Question`
 
 error[E0604]: only `u8` can be cast as `char`, not `u32`
   --> $DIR/error-festival.rs:35:5
diff --git a/src/test/ui/feature-gate-negate-unsigned.stderr b/src/test/ui/feature-gate-negate-unsigned.stderr
index 1025b56f55b..85e9b56e4af 100644
--- a/src/test/ui/feature-gate-negate-unsigned.stderr
+++ b/src/test/ui/feature-gate-negate-unsigned.stderr
@@ -2,13 +2,17 @@ error[E0600]: cannot apply unary operator `-` to type `usize`
   --> $DIR/feature-gate-negate-unsigned.rs:20:23
    |
 LL |     let _max: usize = -1;
-   |                       ^^
+   |                       ^^ cannot apply unary operator `-`
+   |
+   = note: unsigned values cannot be negated
 
 error[E0600]: cannot apply unary operator `-` to type `u8`
   --> $DIR/feature-gate-negate-unsigned.rs:24:14
    |
 LL |     let _y = -x;
-   |              ^^
+   |              ^^ cannot apply unary operator `-`
+   |
+   = note: unsigned values cannot be negated
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/issue-5239-1.stderr b/src/test/ui/issue-5239-1.stderr
index 2f9204e72d3..7ae01fb7d60 100644
--- a/src/test/ui/issue-5239-1.stderr
+++ b/src/test/ui/issue-5239-1.stderr
@@ -5,6 +5,8 @@ LL |     let x = |ref x: isize| { x += 1; };
    |                              -^^^^^
    |                              |
    |                              cannot use `+=` on type `&isize`
+   |
+   = help: `+=` can be used on 'isize', you can dereference `x`: `*x`
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/reachable/expr_unary.stderr b/src/test/ui/reachable/expr_unary.stderr
index 165eccd4239..b889c884fcb 100644
--- a/src/test/ui/reachable/expr_unary.stderr
+++ b/src/test/ui/reachable/expr_unary.stderr
@@ -2,7 +2,7 @@ error[E0600]: cannot apply unary operator `!` to type `!`
   --> $DIR/expr_unary.rs:17:16
    |
 LL |     let x: ! = ! { return; }; //~ ERROR unreachable
-   |                ^^^^^^^^^^^^^
+   |                ^^^^^^^^^^^^^ cannot apply unary operator `!`
 
 error: unreachable expression
   --> $DIR/expr_unary.rs:17:16
diff --git a/src/test/ui/type-check/missing_trait_impl.rs b/src/test/ui/type-check/missing_trait_impl.rs
index adf6b85b642..b3e79ce2447 100644
--- a/src/test/ui/type-check/missing_trait_impl.rs
+++ b/src/test/ui/type-check/missing_trait_impl.rs
@@ -14,3 +14,7 @@ fn main() {
 fn foo<T>(x: T, y: T) {
     let z = x + y; //~ ERROR binary operation `+` cannot be applied to type `T`
 }
+
+fn bar<T>(x: T) {
+    x += x; //~ ERROR binary assignment operation `+=` cannot be applied to type `T`
+}
diff --git a/src/test/ui/type-check/missing_trait_impl.stderr b/src/test/ui/type-check/missing_trait_impl.stderr
index 777f16b12ce..4b01a626814 100644
--- a/src/test/ui/type-check/missing_trait_impl.stderr
+++ b/src/test/ui/type-check/missing_trait_impl.stderr
@@ -6,6 +6,17 @@ LL |     let z = x + y; //~ ERROR binary operation `+` cannot be applied to type
    |
    = note: `T` might need a bound for `std::ops::Add`
 
-error: aborting due to previous error
+error[E0368]: binary assignment operation `+=` cannot be applied to type `T`
+  --> $DIR/missing_trait_impl.rs:19:5
+   |
+LL |     x += x; //~ ERROR binary assignment operation `+=` cannot be applied to type `T`
+   |     -^^^^^
+   |     |
+   |     cannot use `+=` on type `T`
+   |
+   = note: `T` might need a bound for `std::ops::AddAssign`
+
+error: aborting due to 2 previous errors
 
-For more information about this error, try `rustc --explain E0369`.
+Some errors occurred: E0368, E0369.
+For more information about an error, try `rustc --explain E0368`.