diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs index 5abfd2b7d2c..04240bf2875 100644 --- a/src/librustc/middle/liveness.rs +++ b/src/librustc/middle/liveness.rs @@ -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); } diff --git a/src/test/compile-fail/liveness-unused.rs b/src/test/compile-fail/liveness-unused.rs index 0fee48a8c6c..3aab953eb79 100644 --- a/src/test/compile-fail/liveness-unused.rs +++ b/src/test/compile-fail/liveness-unused.rs @@ -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 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 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() { } diff --git a/src/test/run-pass/augmented-assignments.rs b/src/test/run-pass/augmented-assignments.rs index 8c9ebcd274a..3ed9e8548dc 100644 --- a/src/test/run-pass/augmented-assignments.rs +++ b/src/test/run-pass/augmented-assignments.rs @@ -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 for Slice { } } } + +impl<'a> AddAssign for View<'a> { + fn add_assign(&mut self, rhs: i32) { + for lhs in self.0.iter_mut() { + *lhs += rhs; + } + } +}