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:
commit
71f4658c14
3 changed files with 78 additions and 6 deletions
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue