Skip to content

Commit 60538d6

Browse files
committed
Auto merge of rust-lang#5559 - alex-700:fix-while-let-on-iterator-fp, r=flip1995
Fix FP on while-let-on-iterator - fix `is_refutable` for slice patterns - fix `is_refutable` for bindings - add some TODO-s for cases, which can not be fixed easily fixes rust-lang#3780 changelog: fix FP on while-let-on-iterator for arrays and bindings
2 parents cc9088f + d0c1f8a commit 60538d6

File tree

4 files changed

+160
-18
lines changed

4 files changed

+160
-18
lines changed

clippy_lints/src/utils/mod.rs

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -933,6 +933,7 @@ pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_, '_>, expr: &Exp
933933
}
934934

935935
/// Returns `true` if a pattern is refutable.
936+
// TODO: should be implemented using rustc/mir_build/hair machinery
936937
pub fn is_refutable(cx: &LateContext<'_, '_>, pat: &Pat<'_>) -> bool {
937938
fn is_enum_variant(cx: &LateContext<'_, '_>, qpath: &QPath<'_>, id: HirId) -> bool {
938939
matches!(
@@ -946,27 +947,34 @@ pub fn is_refutable(cx: &LateContext<'_, '_>, pat: &Pat<'_>) -> bool {
946947
}
947948

948949
match pat.kind {
949-
PatKind::Binding(..) | PatKind::Wild => false,
950+
PatKind::Wild => false,
951+
PatKind::Binding(_, _, _, pat) => pat.map_or(false, |pat| is_refutable(cx, pat)),
950952
PatKind::Box(ref pat) | PatKind::Ref(ref pat, _) => is_refutable(cx, pat),
951953
PatKind::Lit(..) | PatKind::Range(..) => true,
952954
PatKind::Path(ref qpath) => is_enum_variant(cx, qpath, pat.hir_id),
953-
PatKind::Or(ref pats) | PatKind::Tuple(ref pats, _) => are_refutable(cx, pats.iter().map(|pat| &**pat)),
955+
PatKind::Or(ref pats) => {
956+
// TODO: should be the honest check, that pats is exhaustive set
957+
are_refutable(cx, pats.iter().map(|pat| &**pat))
958+
},
959+
PatKind::Tuple(ref pats, _) => are_refutable(cx, pats.iter().map(|pat| &**pat)),
954960
PatKind::Struct(ref qpath, ref fields, _) => {
955-
if is_enum_variant(cx, qpath, pat.hir_id) {
956-
true
957-
} else {
958-
are_refutable(cx, fields.iter().map(|field| &*field.pat))
959-
}
961+
is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, fields.iter().map(|field| &*field.pat))
960962
},
961963
PatKind::TupleStruct(ref qpath, ref pats, _) => {
962-
if is_enum_variant(cx, qpath, pat.hir_id) {
963-
true
964-
} else {
965-
are_refutable(cx, pats.iter().map(|pat| &**pat))
966-
}
964+
is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, pats.iter().map(|pat| &**pat))
967965
},
968966
PatKind::Slice(ref head, ref middle, ref tail) => {
969-
are_refutable(cx, head.iter().chain(middle).chain(tail.iter()).map(|pat| &**pat))
967+
match &cx.tables.node_type(pat.hir_id).kind {
968+
ty::Slice(..) => {
969+
// [..] is the only irrefutable slice pattern.
970+
!head.is_empty() || middle.is_none() || !tail.is_empty()
971+
},
972+
ty::Array(..) => are_refutable(cx, head.iter().chain(middle).chain(tail.iter()).map(|pat| &**pat)),
973+
_ => {
974+
// unreachable!()
975+
true
976+
},
977+
}
970978
},
971979
}
972980
}

tests/ui/while_let_on_iterator.fixed

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#![warn(clippy::while_let_on_iterator)]
44
#![allow(clippy::never_loop, unreachable_code, unused_mut)]
5+
#![feature(or_patterns)]
56

67
fn base() {
78
let mut iter = 1..20;
@@ -77,6 +78,62 @@ fn refutable() {
7778
// */
7879
}
7980

81+
fn refutable2() {
82+
// Issue 3780
83+
{
84+
let v = vec![1, 2, 3];
85+
let mut it = v.windows(2);
86+
while let Some([x, y]) = it.next() {
87+
println!("x: {}", x);
88+
println!("y: {}", y);
89+
}
90+
91+
let mut it = v.windows(2);
92+
while let Some([x, ..]) = it.next() {
93+
println!("x: {}", x);
94+
}
95+
96+
let mut it = v.windows(2);
97+
while let Some([.., y]) = it.next() {
98+
println!("y: {}", y);
99+
}
100+
101+
let mut it = v.windows(2);
102+
for [..] in it {}
103+
104+
let v = vec![[1], [2], [3]];
105+
let mut it = v.iter();
106+
while let Some([1]) = it.next() {}
107+
108+
let mut it = v.iter();
109+
for [_x] in it {}
110+
}
111+
112+
// binding
113+
{
114+
let v = vec![1, 2, 3];
115+
let mut it = v.iter();
116+
while let Some(x @ 1) = it.next() {
117+
println!("{}", x);
118+
}
119+
120+
let v = vec![[1], [2], [3]];
121+
let mut it = v.iter();
122+
for x @ [_] in it {
123+
println!("{:?}", x);
124+
}
125+
}
126+
127+
// false negative
128+
{
129+
let v = vec![1, 2, 3];
130+
let mut it = v.iter().map(Some);
131+
while let Some(Some(_) | None) = it.next() {
132+
println!("1");
133+
}
134+
}
135+
}
136+
80137
fn nested_loops() {
81138
let a = [42, 1337];
82139
let mut y = a.iter();
@@ -152,6 +209,7 @@ fn issue1654() {
152209
fn main() {
153210
base();
154211
refutable();
212+
refutable2();
155213
nested_loops();
156214
issue1121();
157215
issue2965();

tests/ui/while_let_on_iterator.rs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#![warn(clippy::while_let_on_iterator)]
44
#![allow(clippy::never_loop, unreachable_code, unused_mut)]
5+
#![feature(or_patterns)]
56

67
fn base() {
78
let mut iter = 1..20;
@@ -77,6 +78,62 @@ fn refutable() {
7778
// */
7879
}
7980

81+
fn refutable2() {
82+
// Issue 3780
83+
{
84+
let v = vec![1, 2, 3];
85+
let mut it = v.windows(2);
86+
while let Some([x, y]) = it.next() {
87+
println!("x: {}", x);
88+
println!("y: {}", y);
89+
}
90+
91+
let mut it = v.windows(2);
92+
while let Some([x, ..]) = it.next() {
93+
println!("x: {}", x);
94+
}
95+
96+
let mut it = v.windows(2);
97+
while let Some([.., y]) = it.next() {
98+
println!("y: {}", y);
99+
}
100+
101+
let mut it = v.windows(2);
102+
while let Some([..]) = it.next() {}
103+
104+
let v = vec![[1], [2], [3]];
105+
let mut it = v.iter();
106+
while let Some([1]) = it.next() {}
107+
108+
let mut it = v.iter();
109+
while let Some([_x]) = it.next() {}
110+
}
111+
112+
// binding
113+
{
114+
let v = vec![1, 2, 3];
115+
let mut it = v.iter();
116+
while let Some(x @ 1) = it.next() {
117+
println!("{}", x);
118+
}
119+
120+
let v = vec![[1], [2], [3]];
121+
let mut it = v.iter();
122+
while let Some(x @ [_]) = it.next() {
123+
println!("{:?}", x);
124+
}
125+
}
126+
127+
// false negative
128+
{
129+
let v = vec![1, 2, 3];
130+
let mut it = v.iter().map(Some);
131+
while let Some(Some(_) | None) = it.next() {
132+
println!("1");
133+
}
134+
}
135+
}
136+
80137
fn nested_loops() {
81138
let a = [42, 1337];
82139
let mut y = a.iter();
@@ -152,6 +209,7 @@ fn issue1654() {
152209
fn main() {
153210
base();
154211
refutable();
212+
refutable2();
155213
nested_loops();
156214
issue1121();
157215
issue2965();

tests/ui/while_let_on_iterator.stderr

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,46 @@
11
error: this loop could be written as a `for` loop
2-
--> $DIR/while_let_on_iterator.rs:8:5
2+
--> $DIR/while_let_on_iterator.rs:9:5
33
|
44
LL | while let Option::Some(x) = iter.next() {
55
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in iter`
66
|
77
= note: `-D clippy::while-let-on-iterator` implied by `-D warnings`
88

99
error: this loop could be written as a `for` loop
10-
--> $DIR/while_let_on_iterator.rs:13:5
10+
--> $DIR/while_let_on_iterator.rs:14:5
1111
|
1212
LL | while let Some(x) = iter.next() {
1313
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in iter`
1414

1515
error: this loop could be written as a `for` loop
16-
--> $DIR/while_let_on_iterator.rs:18:5
16+
--> $DIR/while_let_on_iterator.rs:19:5
1717
|
1818
LL | while let Some(_) = iter.next() {}
1919
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in iter`
2020

2121
error: this loop could be written as a `for` loop
22-
--> $DIR/while_let_on_iterator.rs:97:9
22+
--> $DIR/while_let_on_iterator.rs:102:9
23+
|
24+
LL | while let Some([..]) = it.next() {}
25+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for [..] in it`
26+
27+
error: this loop could be written as a `for` loop
28+
--> $DIR/while_let_on_iterator.rs:109:9
29+
|
30+
LL | while let Some([_x]) = it.next() {}
31+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for [_x] in it`
32+
33+
error: this loop could be written as a `for` loop
34+
--> $DIR/while_let_on_iterator.rs:122:9
35+
|
36+
LL | while let Some(x @ [_]) = it.next() {
37+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x @ [_] in it`
38+
39+
error: this loop could be written as a `for` loop
40+
--> $DIR/while_let_on_iterator.rs:154:9
2341
|
2442
LL | while let Some(_) = y.next() {
2543
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in y`
2644

27-
error: aborting due to 4 previous errors
45+
error: aborting due to 7 previous errors
2846

0 commit comments

Comments
 (0)