From 265bc31ae19233bc7df868138fe146607751e963 Mon Sep 17 00:00:00 2001 From: Roman Stoliar Date: Sun, 22 Apr 2018 20:35:21 +0300 Subject: [PATCH 1/4] added missing implementation hint --- src/librustc_typeck/check/op.rs | 161 ++++++++++-------- src/test/ui/codemap_tests/issue-28308.stderr | 2 + src/test/ui/error-codes/E0067.stderr | 2 + src/test/ui/error-codes/E0600.stderr | 2 + src/test/ui/error-festival.stderr | 4 + .../ui/feature-gate-negate-unsigned.stderr | 4 + src/test/ui/issue-5239-1.stderr | 2 + src/test/ui/reachable/expr_unary.stderr | 2 + 8 files changed, 110 insertions(+), 69 deletions(-) diff --git a/src/librustc_typeck/check/op.rs b/src/librustc_typeck/check/op.rs index 4a175248d74a3..66b95f0f4ea18 100644 --- a/src/librustc_typeck/check/op.rs +++ b/src/librustc_typeck/check/op.rs @@ -246,77 +246,92 @@ 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(_, ref ty_mut) = lhs_ty.sty { - if { - !self.infcx.type_moves_by_default(self.param_env, - ty_mut.ty, - lhs_expr.span) && - self.lookup_op_method(ty_mut.ty, - &[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 (mut err, missing_trait) = 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 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 + }; + (err, missing_trait) } - - 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 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 TypeVariants::TyRef(_, ref ty_mut) = lhs_ty.sty { + if { + !self.infcx.type_moves_by_default(self.param_env, + ty_mut.ty, + lhs_expr.span) && + self.lookup_op_method(ty_mut.ty, + &[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())); + } } + (err, missing_trait) + } + }; + 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)); } - err.emit(); } + err.emit(); } self.tcx.types.err } @@ -393,9 +408,17 @@ 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); + 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/codemap_tests/issue-28308.stderr b/src/test/ui/codemap_tests/issue-28308.stderr index 2c8a33d95c03a..b5c2376239d14 100644 --- a/src/test/ui/codemap_tests/issue-28308.stderr +++ b/src/test/ui/codemap_tests/issue-28308.stderr @@ -3,6 +3,8 @@ error[E0600]: cannot apply unary operator `!` to type `&'static str` | LL | assert!("foo"); | ^^^^^^^^^^^^^^^ + | + = note: an implementation of `std::ops::Not` might be missing for `&'static str` 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 76f06c7c46379..43e1ca4096cf6 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 500feb39f5e74..bd79ea79c8b56 100644 --- a/src/test/ui/error-codes/E0600.stderr +++ b/src/test/ui/error-codes/E0600.stderr @@ -3,6 +3,8 @@ error[E0600]: cannot apply unary operator `!` to type `&'static str` | LL | !"a"; //~ ERROR E0600 | ^^^^ + | + = note: an implementation of `std::ops::Not` might be missing for `&'static str` error: aborting due to previous error diff --git a/src/test/ui/error-festival.stderr b/src/test/ui/error-festival.stderr index 345691352b407..6165806aac9c4 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 @@ -29,6 +31,8 @@ error[E0600]: cannot apply unary operator `!` to type `Question` | LL | !Question::Yes; | ^^^^^^^^^^^^^^ + | + = 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 1025b56f55bc3..8831e874a200e 100644 --- a/src/test/ui/feature-gate-negate-unsigned.stderr +++ b/src/test/ui/feature-gate-negate-unsigned.stderr @@ -3,12 +3,16 @@ error[E0600]: cannot apply unary operator `-` to type `usize` | LL | let _max: usize = -1; | ^^ + | + = note: an implementation of `std::ops::Neg` might be missing for `usize` error[E0600]: cannot apply unary operator `-` to type `u8` --> $DIR/feature-gate-negate-unsigned.rs:24:14 | LL | let _y = -x; | ^^ + | + = note: an implementation of `std::ops::Neg` might be missing for `u8` 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 2f9204e72d3fb..adef9848ba743 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` + | + = note: an implementation of `std::ops::AddAssign` might be missing for `&isize` 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 165eccd42396b..7ba21efcd5125 100644 --- a/src/test/ui/reachable/expr_unary.stderr +++ b/src/test/ui/reachable/expr_unary.stderr @@ -3,6 +3,8 @@ error[E0600]: cannot apply unary operator `!` to type `!` | LL | let x: ! = ! { return; }; //~ ERROR unreachable | ^^^^^^^^^^^^^ + | + = note: an implementation of `std::ops::Not` might be missing for `!` error: unreachable expression --> $DIR/expr_unary.rs:17:16 From ecedf44cecbc36478231b16292786f83df45f251 Mon Sep 17 00:00:00 2001 From: Roman Stoliar Date: Tue, 8 May 2018 22:20:41 +0300 Subject: [PATCH 2/4] unary op filter, dereference hint --- src/librustc_typeck/check/op.rs | 131 +++++++++++++----- src/test/ui/binary-op-on-double-ref.stderr | 3 +- src/test/ui/codemap_tests/issue-28308.stderr | 4 +- src/test/ui/error-codes/E0600.stderr | 4 +- src/test/ui/error-festival.stderr | 2 +- .../ui/feature-gate-negate-unsigned.stderr | 8 +- src/test/ui/issue-5239-1.stderr | 2 +- src/test/ui/reachable/expr_unary.stderr | 4 +- 8 files changed, 110 insertions(+), 48 deletions(-) diff --git a/src/librustc_typeck/check/op.rs b/src/librustc_typeck/check/op.rs index 66b95f0f4ea18..86f51ff4f668c 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,7 +246,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { Err(()) => { // error types are considered "builtin" if !lhs_ty.references_error() { - let (mut err, missing_trait) = match is_assign{ + match is_assign{ IsAssign::Yes => { let mut err = struct_span_err!(self.tcx.sess, expr.span, E0368, "binary assignment operation `{}=` \ @@ -269,7 +269,53 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { hir::BiShr => Some("std::ops::ShrAssign"), _ => None }; - (err, missing_trait) + let mut suggested_deref = false; + if let TyRef(_, ref ty_mut) = lhs_ty.sty { + if { + !self.infcx.type_moves_by_default(self.param_env, + ty_mut.ty, + lhs_expr.span) && + self.lookup_op_method(ty_mut.ty, + &[rhs_ty], + Op::Binary(op, is_assign)) + .is_ok() + } { + let codemap = self.tcx.sess.codemap(); + match codemap.span_to_snippet(lhs_expr.span) { + Ok(lstring) =>{ + let msg = &format!( + "`{}=` can be used on '{}', you can \ + dereference `{2}`: `*{2}`", + op.node.as_str(), ty_mut.ty, lstring); + err.help(msg); + suggested_deref = true; + }, + _ => {} + }; + } + } + if let Some(missing_trait) = missing_trait { + if missing_trait == "std::ops::AddAssign" && + 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(); } IsAssign::No => { let mut err = struct_span_err!(self.tcx.sess, expr.span, E0369, @@ -292,7 +338,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { Some("std::cmp::PartialOrd"), _ => None }; - if let TypeVariants::TyRef(_, ref ty_mut) = lhs_ty.sty { + let mut suggested_deref = false; + if let TyRef(_, ref ty_mut) = lhs_ty.sty { if { !self.infcx.type_moves_by_default(self.param_env, ty_mut.ty, @@ -302,36 +349,44 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { Op::Binary(op, is_assign)) .is_ok() } { + let codemap = self.tcx.sess.codemap(); + match codemap.span_to_snippet(lhs_expr.span) { + Ok(lstring) =>{ + let msg = &format!( + "`{}` can be used on '{}', you can \ + dereference `{2}`: `*{2}`", + op.node.as_str(), ty_mut.ty, lstring); + err.help(msg); + suggested_deref = true; + }, + _ =>{} + } + } + } + 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!( - "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())); + &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, missing_trait) - } - }; - 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)); + err.emit(); } } - err.emit(); } self.tcx.types.err } @@ -411,13 +466,27 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { let mut err = struct_span_err!(self.tcx.sess, ex.span, E0600, "cannot apply unary operator `{}` to type `{}`", op.as_str(), actual); + err.span_label(ex.span, format!("cannot apply unary \ + operator `{}`", op.as_str())); 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 `{}`", + 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.ty.sty == TyStr => {}, + _ => { + 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 07aa3bfe40dea..020d74bee5215 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 b5c2376239d14..15c159a3b153c 100644 --- a/src/test/ui/codemap_tests/issue-28308.stderr +++ b/src/test/ui/codemap_tests/issue-28308.stderr @@ -2,9 +2,7 @@ error[E0600]: cannot apply unary operator `!` to type `&'static str` --> $DIR/issue-28308.rs:12:5 | LL | assert!("foo"); - | ^^^^^^^^^^^^^^^ - | - = note: an implementation of `std::ops::Not` might be missing for `&'static str` + | ^^^^^^^^^^^^^^^ cannot apply unary operator `!` error: aborting due to previous error diff --git a/src/test/ui/error-codes/E0600.stderr b/src/test/ui/error-codes/E0600.stderr index bd79ea79c8b56..c29ec4fe6ae76 100644 --- a/src/test/ui/error-codes/E0600.stderr +++ b/src/test/ui/error-codes/E0600.stderr @@ -2,9 +2,7 @@ error[E0600]: cannot apply unary operator `!` to type `&'static str` --> $DIR/E0600.rs:12:5 | LL | !"a"; //~ ERROR E0600 - | ^^^^ - | - = note: an implementation of `std::ops::Not` might be missing for `&'static str` + | ^^^^ 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 6165806aac9c4..69f11b4b7c08c 100644 --- a/src/test/ui/error-festival.stderr +++ b/src/test/ui/error-festival.stderr @@ -30,7 +30,7 @@ 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` diff --git a/src/test/ui/feature-gate-negate-unsigned.stderr b/src/test/ui/feature-gate-negate-unsigned.stderr index 8831e874a200e..85e9b56e4af9d 100644 --- a/src/test/ui/feature-gate-negate-unsigned.stderr +++ b/src/test/ui/feature-gate-negate-unsigned.stderr @@ -2,17 +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: an implementation of `std::ops::Neg` might be missing for `usize` + = 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: an implementation of `std::ops::Neg` might be missing for `u8` + = 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 adef9848ba743..7ae01fb7d6012 100644 --- a/src/test/ui/issue-5239-1.stderr +++ b/src/test/ui/issue-5239-1.stderr @@ -6,7 +6,7 @@ LL | let x = |ref x: isize| { x += 1; }; | | | cannot use `+=` on type `&isize` | - = note: an implementation of `std::ops::AddAssign` might be missing for `&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 7ba21efcd5125..b889c884fcbb2 100644 --- a/src/test/ui/reachable/expr_unary.stderr +++ b/src/test/ui/reachable/expr_unary.stderr @@ -2,9 +2,7 @@ error[E0600]: cannot apply unary operator `!` to type `!` --> $DIR/expr_unary.rs:17:16 | LL | let x: ! = ! { return; }; //~ ERROR unreachable - | ^^^^^^^^^^^^^ - | - = note: an implementation of `std::ops::Not` might be missing for `!` + | ^^^^^^^^^^^^^ cannot apply unary operator `!` error: unreachable expression --> $DIR/expr_unary.rs:17:16 From 127d2434932ec25892435ae65aeaa49390f38e1b Mon Sep 17 00:00:00 2001 From: Roman Stoliar Date: Wed, 9 May 2018 10:47:09 +0300 Subject: [PATCH 3/4] [wip] fixed some error, added missing test --- src/librustc_typeck/check/op.rs | 133 ++++++++---------- src/test/ui/type-check/missing_trait_impl.rs | 4 + .../ui/type-check/missing_trait_impl.stderr | 15 +- 3 files changed, 77 insertions(+), 75 deletions(-) diff --git a/src/librustc_typeck/check/op.rs b/src/librustc_typeck/check/op.rs index 86f51ff4f668c..29adac324ef48 100644 --- a/src/librustc_typeck/check/op.rs +++ b/src/librustc_typeck/check/op.rs @@ -246,7 +246,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { Err(()) => { // error types are considered "builtin" if !lhs_ty.references_error() { - match is_assign{ + 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 `{}=` \ @@ -256,19 +257,6 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { err.span_label(lhs_expr.span, format!("cannot use `{}=` on type `{}`", op.node.as_str(), lhs_ty)); - 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 - }; let mut suggested_deref = false; if let TyRef(_, ref ty_mut) = lhs_ty.sty { if { @@ -280,22 +268,31 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { Op::Binary(op, is_assign)) .is_ok() } { - let codemap = self.tcx.sess.codemap(); - match codemap.span_to_snippet(lhs_expr.span) { - Ok(lstring) =>{ - let msg = &format!( + if let Ok(lstring) = codemap.span_to_snippet(lhs_expr.span) { + let msg = &format!( "`{}=` can be used on '{}', you can \ dereference `{2}`: `*{2}`", op.node.as_str(), ty_mut.ty, lstring); - err.help(msg); - suggested_deref = true; - }, - _ => {} - }; + 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 missing_trait == "std::ops::AddAssign" && + 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 @@ -306,13 +303,11 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { 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)); - } + } else if !suggested_deref { + err.note( + &format!("an implementation of `{}` might \ + be missing for `{}`", + missing_trait, lhs_ty)); } } err.emit(); @@ -322,22 +317,6 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { "binary operation `{}` cannot be applied to type `{}`", op.node.as_str(), lhs_ty); - 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 - }; let mut suggested_deref = false; if let TyRef(_, ref ty_mut) = lhs_ty.sty { if { @@ -349,22 +328,34 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { Op::Binary(op, is_assign)) .is_ok() } { - let codemap = self.tcx.sess.codemap(); - match codemap.span_to_snippet(lhs_expr.span) { - Ok(lstring) =>{ - let msg = &format!( + if let Ok(lstring) = codemap.span_to_snippet(lhs_expr.span) { + let msg = &format!( "`{}` can be used on '{}', you can \ dereference `{2}`: `*{2}`", op.node.as_str(), ty_mut.ty, lstring); - err.help(msg); - suggested_deref = true; - }, - _ =>{} + 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 missing_trait == "std::ops::Add" && + 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 @@ -375,13 +366,11 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { 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)); - } + } else if !suggested_deref { + err.note( + &format!("an implementation of `{}` might \ + be missing for `{}`", + missing_trait, lhs_ty)); } } err.emit(); @@ -468,20 +457,18 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { op.as_str(), actual); err.span_label(ex.span, format!("cannot apply unary \ operator `{}`", op.as_str())); - let missing_trait = match op { - hir::UnNeg => "std::ops::Neg", - hir::UnNot => "std::ops::Not", - hir::UnDeref => "std::ops::UnDerf" - }; - match actual.sty{ - TyUint(_) => { - if op == hir::UnNeg{ - err.note(&format!("unsigned values cannot be negated")); - } + 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.ty.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)); diff --git a/src/test/ui/type-check/missing_trait_impl.rs b/src/test/ui/type-check/missing_trait_impl.rs index adf6b85b6429c..b3e79ce2447fb 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(x: T, y: T) { let z = x + y; //~ ERROR binary operation `+` cannot be applied to type `T` } + +fn bar(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 777f16b12ce68..4b01a626814e5 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`. From fa8ac4aac845b1abaf7e8c969432f14ace68d037 Mon Sep 17 00:00:00 2001 From: Roman Stoliar Date: Wed, 9 May 2018 12:38:09 +0300 Subject: [PATCH 4/4] fixed double ref hint --- src/librustc_typeck/check/op.rs | 52 ++++++++++++++-------- src/test/ui/binary-op-on-double-ref.stderr | 2 +- 2 files changed, 35 insertions(+), 19 deletions(-) diff --git a/src/librustc_typeck/check/op.rs b/src/librustc_typeck/check/op.rs index 29adac324ef48..0c717b9f20125 100644 --- a/src/librustc_typeck/check/op.rs +++ b/src/librustc_typeck/check/op.rs @@ -258,7 +258,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { format!("cannot use `{}=` on type `{}`", op.node.as_str(), lhs_ty)); let mut suggested_deref = false; - if let TyRef(_, ref ty_mut) = lhs_ty.sty { + if let TyRef(_, mut ty_mut) = lhs_ty.sty { if { !self.infcx.type_moves_by_default(self.param_env, ty_mut.ty, @@ -269,10 +269,16 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { .is_ok() } { if let Ok(lstring) = codemap.span_to_snippet(lhs_expr.span) { + while let TyRef(_, ty_mut_inner) = ty_mut.ty.sty{ + ty_mut = ty_mut_inner; + } let msg = &format!( "`{}=` can be used on '{}', you can \ dereference `{2}`: `*{2}`", - op.node.as_str(), ty_mut.ty, lstring); + op.node.as_str(), + ty_mut.ty, + lstring + ); err.help(msg); suggested_deref = true; } @@ -300,14 +306,16 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // 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)); + 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.note(&format!( + "an implementation of `{}` might \ + be missing for `{}`", + missing_trait, lhs_ty + )); } } err.emit(); @@ -318,7 +326,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { op.node.as_str(), lhs_ty); let mut suggested_deref = false; - if let TyRef(_, ref ty_mut) = lhs_ty.sty { + if let TyRef(_, mut ty_mut) = lhs_ty.sty { if { !self.infcx.type_moves_by_default(self.param_env, ty_mut.ty, @@ -329,10 +337,16 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { .is_ok() } { if let Ok(lstring) = codemap.span_to_snippet(lhs_expr.span) { + while let TyRef(_, ty_mut_inner) = ty_mut.ty.sty{ + ty_mut = ty_mut_inner; + } let msg = &format!( "`{}` can be used on '{}', you can \ dereference `{2}`: `*{2}`", - op.node.as_str(), ty_mut.ty, lstring); + op.node.as_str(), + ty_mut.ty, + lstring + ); err.help(msg); suggested_deref = true; } @@ -363,14 +377,16 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // 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)); + 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.note(&format!( + "an implementation of `{}` might \ + be missing for `{}`", + missing_trait, lhs_ty + )); } } err.emit(); diff --git a/src/test/ui/binary-op-on-double-ref.stderr b/src/test/ui/binary-op-on-double-ref.stderr index 020d74bee5215..c89defa3dd196 100644 --- a/src/test/ui/binary-op-on-double-ref.stderr +++ b/src/test/ui/binary-op-on-double-ref.stderr @@ -4,7 +4,7 @@ error[E0369]: binary operation `%` cannot be applied to type `&&{integer}` LL | x % 2 == 0 | ^^^^^ | - = help: `%` can be used on '&{integer}', you can dereference `x`: `*x` + = help: `%` can be used on '{integer}', you can dereference `x`: `*x` error: aborting due to previous error