Skip to content

Commit 18ab97d

Browse files
committed
Auto merge of rust-lang#8668 - Jarcho:iter_with_drain_8538, r=Manishearth
Don't lint `iter_with_drain` on references fixes rust-lang#8538 changelog: Don't lint `iter_with_drain` on references
2 parents 5c19ae9 + 744b0ff commit 18ab97d

File tree

3 files changed

+51
-58
lines changed

3 files changed

+51
-58
lines changed
Lines changed: 33 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,72 +1,47 @@
11
use clippy_utils::diagnostics::span_lint_and_sugg;
2+
use clippy_utils::higher::Range;
23
use clippy_utils::is_integer_const;
3-
use clippy_utils::ty::is_type_diagnostic_item;
4-
use clippy_utils::{
5-
higher::{self, Range},
6-
SpanlessEq,
7-
};
84
use rustc_ast::ast::RangeLimits;
95
use rustc_errors::Applicability;
106
use rustc_hir::{Expr, ExprKind, QPath};
117
use rustc_lint::LateContext;
12-
use rustc_span::symbol::{sym, Symbol};
8+
use rustc_span::symbol::sym;
139
use rustc_span::Span;
1410

1511
use super::ITER_WITH_DRAIN;
1612

17-
const DRAIN_TYPES: &[Symbol] = &[sym::Vec, sym::VecDeque];
18-
1913
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span, arg: &Expr<'_>) {
20-
let ty = cx.typeck_results().expr_ty(recv).peel_refs();
21-
if let Some(drained_type) = DRAIN_TYPES.iter().find(|&&sym| is_type_diagnostic_item(cx, ty, sym)) {
22-
// Refuse to emit `into_iter` suggestion on draining struct fields due
23-
// to the strong possibility of processing unmovable field.
24-
if let ExprKind::Field(..) = recv.kind {
25-
return;
26-
}
14+
if !matches!(recv.kind, ExprKind::Field(..))
15+
&& let Some(adt) = cx.typeck_results().expr_ty(recv).ty_adt_def()
16+
&& let Some(ty_name) = cx.tcx.get_diagnostic_name(adt.did())
17+
&& matches!(ty_name, sym::Vec | sym::VecDeque)
18+
&& let Some(range) = Range::hir(arg)
19+
&& is_full_range(cx, recv, range)
20+
{
21+
span_lint_and_sugg(
22+
cx,
23+
ITER_WITH_DRAIN,
24+
span.with_hi(expr.span.hi()),
25+
&format!("`drain(..)` used on a `{}`", ty_name),
26+
"try this",
27+
"into_iter()".to_string(),
28+
Applicability::MaybeIncorrect,
29+
);
30+
};
31+
}
2732

28-
if let Some(range) = higher::Range::hir(arg) {
29-
let left_full = match range {
30-
Range { start: Some(start), .. } if is_integer_const(cx, start, 0) => true,
31-
Range { start: None, .. } => true,
32-
_ => false,
33-
};
34-
let full = left_full
35-
&& match range {
36-
Range {
37-
end: Some(end),
38-
limits: RangeLimits::HalfOpen,
39-
..
40-
} => {
41-
// `x.drain(..x.len())` call
42-
if_chain! {
43-
if let ExprKind::MethodCall(len_path, len_args, _) = end.kind;
44-
if len_path.ident.name == sym::len && len_args.len() == 1;
45-
if let ExprKind::Path(QPath::Resolved(_, drain_path)) = recv.kind;
46-
if let ExprKind::Path(QPath::Resolved(_, len_path)) = len_args[0].kind;
47-
if SpanlessEq::new(cx).eq_path(drain_path, len_path);
48-
then { true }
49-
else { false }
50-
}
51-
},
52-
Range {
53-
end: None,
54-
limits: RangeLimits::HalfOpen,
55-
..
56-
} => true,
57-
_ => false,
58-
};
59-
if full {
60-
span_lint_and_sugg(
61-
cx,
62-
ITER_WITH_DRAIN,
63-
span.with_hi(expr.span.hi()),
64-
&format!("`drain(..)` used on a `{}`", drained_type),
65-
"try this",
66-
"into_iter()".to_string(),
67-
Applicability::MaybeIncorrect,
68-
);
33+
fn is_full_range(cx: &LateContext<'_>, container: &Expr<'_>, range: Range<'_>) -> bool {
34+
range.start.map_or(true, |e| is_integer_const(cx, e, 0))
35+
&& range.end.map_or(true, |e| {
36+
if range.limits == RangeLimits::HalfOpen
37+
&& let ExprKind::Path(QPath::Resolved(None, container_path)) = container.kind
38+
&& let ExprKind::MethodCall(name, [self_arg], _) = e.kind
39+
&& name.ident.name == sym::len
40+
&& let ExprKind::Path(QPath::Resolved(None, path)) = self_arg.kind
41+
{
42+
container_path.res == path.res
43+
} else {
44+
false
6945
}
70-
}
71-
}
46+
})
7247
}

tests/ui/iter_with_drain.fixed

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,15 @@ fn should_not_help() {
3939
let _: Vec<_> = b.drain(0..a.len()).collect();
4040
}
4141

42+
fn _closed_range(mut x: Vec<String>) {
43+
let _: Vec<String> = x.drain(0..=x.len()).collect();
44+
}
45+
46+
fn _with_mut(x: &mut Vec<String>, y: &mut VecDeque<String>) {
47+
let _: Vec<String> = x.drain(..).collect();
48+
let _: Vec<String> = y.drain(..).collect();
49+
}
50+
4251
#[derive(Default)]
4352
struct Bomb {
4453
fire: Vec<u8>,

tests/ui/iter_with_drain.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,15 @@ fn should_not_help() {
3939
let _: Vec<_> = b.drain(0..a.len()).collect();
4040
}
4141

42+
fn _closed_range(mut x: Vec<String>) {
43+
let _: Vec<String> = x.drain(0..=x.len()).collect();
44+
}
45+
46+
fn _with_mut(x: &mut Vec<String>, y: &mut VecDeque<String>) {
47+
let _: Vec<String> = x.drain(..).collect();
48+
let _: Vec<String> = y.drain(..).collect();
49+
}
50+
4251
#[derive(Default)]
4352
struct Bomb {
4453
fire: Vec<u8>,

0 commit comments

Comments
 (0)