Skip to content

Commit b43e625

Browse files
authored
Better check for assign_op_pattern in const context (#15532)
`assign_op_pattern` can be used in a `const` context if the trait definition as well as the implementation of the corresponding `Assign` pattern is `const` as well. The lint was temporarily deactivated in the compiler repository for all `const` contexts to avoid false positives when operators were potentially constified. This reenables it when appropriate now that the repositories have been synced. Closes #15285 changelog: [`assign_op_pattern`]: trigger in `const` contexts when appropriate r? @flip1995
2 parents 228f064 + 86beecc commit b43e625

File tree

4 files changed

+133
-24
lines changed

4 files changed

+133
-24
lines changed

clippy_lints/src/operators/assign_op_pattern.rs

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use clippy_utils::diagnostics::span_lint_and_then;
22
use clippy_utils::msrvs::Msrv;
3+
use clippy_utils::qualify_min_const_fn::is_stable_const_fn;
34
use clippy_utils::source::SpanRangeExt;
45
use clippy_utils::ty::implements_trait;
56
use clippy_utils::visitors::for_each_expr_without_closures;
@@ -20,7 +21,7 @@ pub(super) fn check<'tcx>(
2021
expr: &'tcx hir::Expr<'_>,
2122
assignee: &'tcx hir::Expr<'_>,
2223
e: &'tcx hir::Expr<'_>,
23-
_msrv: Msrv,
24+
msrv: Msrv,
2425
) {
2526
if let hir::ExprKind::Binary(op, l, r) = &e.kind {
2627
let lint = |assignee: &hir::Expr<'_>, rhs: &hir::Expr<'_>| {
@@ -43,10 +44,28 @@ pub(super) fn check<'tcx>(
4344
}
4445
}
4546

46-
// Skip if the trait is not stable in const contexts
47-
// FIXME: reintroduce a better check after this is merged back into Clippy
47+
// Skip if the trait or the implementation is not stable in const contexts
4848
if is_in_const_context(cx) {
49-
return;
49+
if cx
50+
.tcx
51+
.associated_item_def_ids(trait_id)
52+
.first()
53+
.is_none_or(|binop_id| !is_stable_const_fn(cx, *binop_id, msrv))
54+
{
55+
return;
56+
}
57+
58+
let impls = cx.tcx.non_blanket_impls_for_ty(trait_id, rty).collect::<Vec<_>>();
59+
if impls.is_empty()
60+
|| impls.into_iter().any(|impl_id| {
61+
cx.tcx
62+
.associated_item_def_ids(impl_id)
63+
.first()
64+
.is_none_or(|fn_id| !is_stable_const_fn(cx, *fn_id, msrv))
65+
})
66+
{
67+
return;
68+
}
5069
}
5170

5271
span_lint_and_then(

tests/ui/assign_ops.fixed

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
#![allow(clippy::useless_vec)]
22
#![warn(clippy::assign_op_pattern)]
3-
#![feature(const_trait_impl, const_ops)]
3+
#![feature(const_ops)]
4+
#![feature(const_trait_impl)]
45

5-
use core::num::Wrapping;
6+
use std::num::Wrapping;
67
use std::ops::{Mul, MulAssign};
78

89
fn main() {
@@ -82,6 +83,18 @@ mod issue14871 {
8283
pub trait Number: Copy + Add<Self, Output = Self> + AddAssign {
8384
const ZERO: Self;
8485
const ONE: Self;
86+
87+
fn non_constant(value: usize) -> Self {
88+
let mut res = Self::ZERO;
89+
let mut count = 0;
90+
while count < value {
91+
res += Self::ONE;
92+
//~^ assign_op_pattern
93+
count += 1;
94+
//~^ assign_op_pattern
95+
}
96+
res
97+
}
8598
}
8699

87100
#[rustfmt::skip] // rustfmt doesn't understand the order of pub const on traits (yet)
@@ -91,16 +104,36 @@ mod issue14871 {
91104

92105
impl<T> const NumberConstants for T
93106
where
94-
T: Number + [const] core::ops::Add,
107+
T: Number + [const] std::ops::Add,
95108
{
96109
fn constant(value: usize) -> Self {
97110
let mut res = Self::ZERO;
98111
let mut count = 0;
99112
while count < value {
100113
res = res + Self::ONE;
101114
count += 1;
115+
//~^ assign_op_pattern
102116
}
103117
res
104118
}
105119
}
120+
121+
pub struct S;
122+
123+
impl const std::ops::Add for S {
124+
type Output = S;
125+
fn add(self, _rhs: S) -> S {
126+
S
127+
}
128+
}
129+
130+
impl const std::ops::AddAssign for S {
131+
fn add_assign(&mut self, rhs: S) {}
132+
}
133+
134+
const fn do_add() {
135+
let mut s = S;
136+
s += S;
137+
//~^ assign_op_pattern
138+
}
106139
}

tests/ui/assign_ops.rs

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
#![allow(clippy::useless_vec)]
22
#![warn(clippy::assign_op_pattern)]
3-
#![feature(const_trait_impl, const_ops)]
3+
#![feature(const_ops)]
4+
#![feature(const_trait_impl)]
45

5-
use core::num::Wrapping;
6+
use std::num::Wrapping;
67
use std::ops::{Mul, MulAssign};
78

89
fn main() {
@@ -82,6 +83,18 @@ mod issue14871 {
8283
pub trait Number: Copy + Add<Self, Output = Self> + AddAssign {
8384
const ZERO: Self;
8485
const ONE: Self;
86+
87+
fn non_constant(value: usize) -> Self {
88+
let mut res = Self::ZERO;
89+
let mut count = 0;
90+
while count < value {
91+
res = res + Self::ONE;
92+
//~^ assign_op_pattern
93+
count = count + 1;
94+
//~^ assign_op_pattern
95+
}
96+
res
97+
}
8598
}
8699

87100
#[rustfmt::skip] // rustfmt doesn't understand the order of pub const on traits (yet)
@@ -91,16 +104,36 @@ mod issue14871 {
91104

92105
impl<T> const NumberConstants for T
93106
where
94-
T: Number + [const] core::ops::Add,
107+
T: Number + [const] std::ops::Add,
95108
{
96109
fn constant(value: usize) -> Self {
97110
let mut res = Self::ZERO;
98111
let mut count = 0;
99112
while count < value {
100113
res = res + Self::ONE;
101-
count += 1;
114+
count = count + 1;
115+
//~^ assign_op_pattern
102116
}
103117
res
104118
}
105119
}
120+
121+
pub struct S;
122+
123+
impl const std::ops::Add for S {
124+
type Output = S;
125+
fn add(self, _rhs: S) -> S {
126+
S
127+
}
128+
}
129+
130+
impl const std::ops::AddAssign for S {
131+
fn add_assign(&mut self, rhs: S) {}
132+
}
133+
134+
const fn do_add() {
135+
let mut s = S;
136+
s = s + S;
137+
//~^ assign_op_pattern
138+
}
106139
}

tests/ui/assign_ops.stderr

Lines changed: 37 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: manual implementation of an assign operation
2-
--> tests/ui/assign_ops.rs:10:5
2+
--> tests/ui/assign_ops.rs:11:5
33
|
44
LL | a = a + 1;
55
| ^^^^^^^^^ help: replace it with: `a += 1`
@@ -8,70 +8,94 @@ LL | a = a + 1;
88
= help: to override `-D warnings` add `#[allow(clippy::assign_op_pattern)]`
99

1010
error: manual implementation of an assign operation
11-
--> tests/ui/assign_ops.rs:12:5
11+
--> tests/ui/assign_ops.rs:13:5
1212
|
1313
LL | a = 1 + a;
1414
| ^^^^^^^^^ help: replace it with: `a += 1`
1515

1616
error: manual implementation of an assign operation
17-
--> tests/ui/assign_ops.rs:14:5
17+
--> tests/ui/assign_ops.rs:15:5
1818
|
1919
LL | a = a - 1;
2020
| ^^^^^^^^^ help: replace it with: `a -= 1`
2121

2222
error: manual implementation of an assign operation
23-
--> tests/ui/assign_ops.rs:16:5
23+
--> tests/ui/assign_ops.rs:17:5
2424
|
2525
LL | a = a * 99;
2626
| ^^^^^^^^^^ help: replace it with: `a *= 99`
2727

2828
error: manual implementation of an assign operation
29-
--> tests/ui/assign_ops.rs:18:5
29+
--> tests/ui/assign_ops.rs:19:5
3030
|
3131
LL | a = 42 * a;
3232
| ^^^^^^^^^^ help: replace it with: `a *= 42`
3333

3434
error: manual implementation of an assign operation
35-
--> tests/ui/assign_ops.rs:20:5
35+
--> tests/ui/assign_ops.rs:21:5
3636
|
3737
LL | a = a / 2;
3838
| ^^^^^^^^^ help: replace it with: `a /= 2`
3939

4040
error: manual implementation of an assign operation
41-
--> tests/ui/assign_ops.rs:22:5
41+
--> tests/ui/assign_ops.rs:23:5
4242
|
4343
LL | a = a % 5;
4444
| ^^^^^^^^^ help: replace it with: `a %= 5`
4545

4646
error: manual implementation of an assign operation
47-
--> tests/ui/assign_ops.rs:24:5
47+
--> tests/ui/assign_ops.rs:25:5
4848
|
4949
LL | a = a & 1;
5050
| ^^^^^^^^^ help: replace it with: `a &= 1`
5151

5252
error: manual implementation of an assign operation
53-
--> tests/ui/assign_ops.rs:31:5
53+
--> tests/ui/assign_ops.rs:32:5
5454
|
5555
LL | s = s + "bla";
5656
| ^^^^^^^^^^^^^ help: replace it with: `s += "bla"`
5757

5858
error: manual implementation of an assign operation
59-
--> tests/ui/assign_ops.rs:36:5
59+
--> tests/ui/assign_ops.rs:37:5
6060
|
6161
LL | a = a + Wrapping(1u32);
6262
| ^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `a += Wrapping(1u32)`
6363

6464
error: manual implementation of an assign operation
65-
--> tests/ui/assign_ops.rs:39:5
65+
--> tests/ui/assign_ops.rs:40:5
6666
|
6767
LL | v[0] = v[0] + v[1];
6868
| ^^^^^^^^^^^^^^^^^^ help: replace it with: `v[0] += v[1]`
6969

7070
error: manual implementation of an assign operation
71-
--> tests/ui/assign_ops.rs:52:5
71+
--> tests/ui/assign_ops.rs:53:5
7272
|
7373
LL | buf = buf + cows.clone();
7474
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `buf += cows.clone()`
7575

76-
error: aborting due to 12 previous errors
76+
error: manual implementation of an assign operation
77+
--> tests/ui/assign_ops.rs:91:17
78+
|
79+
LL | res = res + Self::ONE;
80+
| ^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `res += Self::ONE`
81+
82+
error: manual implementation of an assign operation
83+
--> tests/ui/assign_ops.rs:93:17
84+
|
85+
LL | count = count + 1;
86+
| ^^^^^^^^^^^^^^^^^ help: replace it with: `count += 1`
87+
88+
error: manual implementation of an assign operation
89+
--> tests/ui/assign_ops.rs:114:17
90+
|
91+
LL | count = count + 1;
92+
| ^^^^^^^^^^^^^^^^^ help: replace it with: `count += 1`
93+
94+
error: manual implementation of an assign operation
95+
--> tests/ui/assign_ops.rs:136:9
96+
|
97+
LL | s = s + S;
98+
| ^^^^^^^^^ help: replace it with: `s += S`
99+
100+
error: aborting due to 16 previous errors
77101

0 commit comments

Comments
 (0)