]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/methods/iter_with_drain.rs
Rollup merge of #94605 - Michcioperz:patch-1, r=pnkfelix
[rust.git] / src / tools / clippy / clippy_lints / src / methods / iter_with_drain.rs
1 use clippy_utils::diagnostics::span_lint_and_sugg;
2 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 };
8 use rustc_ast::ast::RangeLimits;
9 use rustc_errors::Applicability;
10 use rustc_hir::{Expr, ExprKind, QPath};
11 use rustc_lint::LateContext;
12 use rustc_span::symbol::{sym, Symbol};
13 use rustc_span::Span;
14
15 use super::ITER_WITH_DRAIN;
16
17 const DRAIN_TYPES: &[Symbol] = &[sym::Vec, sym::VecDeque];
18
19 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         }
27
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                 );
69             }
70         }
71     }
72 }