Auto merge of #32048 - bluss:overloaded-assign-op, r=eddyb

Do not trigger unused_assignments for overloaded AssignOps

If `v` were a type with some kind of indirection, so that `v += 1` would
have an effect even if `v` were not used anymore, the unused_assignments lint
would mark a false positive.

This exempts overloaded (non-primitive) assign ops from being treated as
assignments (they are method calls).

The previous compile-fail tests that ensure x += 1 can trigger for
primitive types continue to pass. Added a representative test for the
"view" indirection.

Fixes #31895
This commit is contained in:
bors 2016-03-06 15:10:44 +00:00
commit 71f4658c14
3 changed files with 78 additions and 6 deletions

View file

@ -1086,11 +1086,17 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
}
hir::ExprAssignOp(_, ref l, ref r) => {
// see comment on lvalues in
// propagate_through_lvalue_components()
let succ = self.write_lvalue(&l, succ, ACC_WRITE|ACC_READ);
let succ = self.propagate_through_expr(&r, succ);
self.propagate_through_lvalue_components(&l, succ)
// an overloaded assign op is like a method call
if self.ir.tcx.is_method_call(expr.id) {
let succ = self.propagate_through_expr(&l, succ);
self.propagate_through_expr(&r, succ)
} else {
// see comment on lvalues in
// propagate_through_lvalue_components()
let succ = self.write_lvalue(&l, succ, ACC_WRITE|ACC_READ);
let succ = self.propagate_through_expr(&r, succ);
self.propagate_through_lvalue_components(&l, succ)
}
}
// Uninteresting cases: just propagate in rev exec order
@ -1410,7 +1416,9 @@ fn check_expr(this: &mut Liveness, expr: &Expr) {
}
hir::ExprAssignOp(_, ref l, _) => {
this.check_lvalue(&l);
if !this.ir.tcx.is_method_call(expr.id) {
this.check_lvalue(&l);
}
intravisit::walk_expr(this, expr);
}

View file

@ -12,6 +12,8 @@
#![deny(unused_assignments)]
#![allow(dead_code, non_camel_case_types, trivial_numeric_casts)]
use std::ops::AddAssign;
fn f1(x: isize) {
//~^ ERROR unused variable: `x`
}
@ -100,5 +102,49 @@ fn f5c() {
}
}
struct View<'a>(&'a mut [i32]);
impl<'a> AddAssign<i32> for View<'a> {
fn add_assign(&mut self, rhs: i32) {
for lhs in self.0.iter_mut() {
*lhs += rhs;
}
}
}
fn f6() {
let mut array = [1, 2, 3];
let mut v = View(&mut array);
// ensure an error shows up for x even if lhs of an overloaded add assign
let x;
//~^ ERROR variable `x` is assigned to, but never used
*({
x = 0; //~ ERROR value assigned to `x` is never read
&mut v
}) += 1;
}
struct MutRef<'a>(&'a mut i32);
impl<'a> AddAssign<i32> for MutRef<'a> {
fn add_assign(&mut self, rhs: i32) {
*self.0 += rhs;
}
}
fn f7() {
let mut a = 1;
{
// `b` does not trigger unused_variables
let mut b = MutRef(&mut a);
b += 1;
}
drop(a);
}
fn main() {
}

View file

@ -8,6 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![deny(unused_assignments)]
use std::mem;
use std::ops::{
AddAssign, BitAndAssign, BitOrAssign, BitXorAssign, DivAssign, Index, MulAssign, RemAssign,
@ -27,6 +29,8 @@ impl Slice {
}
}
struct View<'a>(&'a mut [i32]);
fn main() {
let mut x = Int(1);
@ -78,6 +82,12 @@ fn main() {
assert_eq!(array[0], 1);
assert_eq!(array[1], 2);
assert_eq!(array[2], 3);
// sized indirection
// check that this does *not* trigger the unused_assignments lint
let mut array = [0, 1, 2];
let mut view = View(&mut array);
view += 1;
}
impl AddAssign for Int {
@ -159,3 +169,11 @@ impl AddAssign<i32> for Slice {
}
}
}
impl<'a> AddAssign<i32> for View<'a> {
fn add_assign(&mut self, rhs: i32) {
for lhs in self.0.iter_mut() {
*lhs += rhs;
}
}
}