|  | 
|  | 1 | +use crate::methods::DRAIN_COLLECT; | 
|  | 2 | +use clippy_utils::diagnostics::span_lint_and_sugg; | 
|  | 3 | +use clippy_utils::is_range_full; | 
|  | 4 | +use clippy_utils::source::snippet; | 
|  | 5 | +use clippy_utils::ty::is_type_lang_item; | 
|  | 6 | +use rustc_errors::Applicability; | 
|  | 7 | +use rustc_hir::Expr; | 
|  | 8 | +use rustc_hir::ExprKind; | 
|  | 9 | +use rustc_hir::LangItem; | 
|  | 10 | +use rustc_hir::Path; | 
|  | 11 | +use rustc_hir::QPath; | 
|  | 12 | +use rustc_lint::LateContext; | 
|  | 13 | +use rustc_middle::query::Key; | 
|  | 14 | +use rustc_middle::ty::Ty; | 
|  | 15 | +use rustc_span::sym; | 
|  | 16 | +use rustc_span::Symbol; | 
|  | 17 | + | 
|  | 18 | +/// Checks if both types match the given diagnostic item, e.g.: | 
|  | 19 | +/// | 
|  | 20 | +/// `vec![1,2].drain(..).collect::<Vec<_>>()` | 
|  | 21 | +///  ^^^^^^^^^                     ^^^^^^   true | 
|  | 22 | +/// `vec![1,2].drain(..).collect::<HashSet<_>>()` | 
|  | 23 | +///  ^^^^^^^^^                     ^^^^^^^^^^  false | 
|  | 24 | +fn types_match_diagnostic_item(cx: &LateContext<'_>, expr: Ty<'_>, recv: Ty<'_>, sym: Symbol) -> bool { | 
|  | 25 | +    if let Some(expr_adt_did) = expr.ty_adt_id() | 
|  | 26 | +        && let Some(recv_adt_did) = recv.ty_adt_id() | 
|  | 27 | +    { | 
|  | 28 | +        cx.tcx.is_diagnostic_item(sym, expr_adt_did) && cx.tcx.is_diagnostic_item(sym, recv_adt_did) | 
|  | 29 | +    } else { | 
|  | 30 | +        false | 
|  | 31 | +    } | 
|  | 32 | +} | 
|  | 33 | + | 
|  | 34 | +/// Checks `std::{vec::Vec, collections::VecDeque}`. | 
|  | 35 | +fn check_vec(cx: &LateContext<'_>, args: &[Expr<'_>], expr: Ty<'_>, recv: Ty<'_>, recv_path: &Path<'_>) -> bool { | 
|  | 36 | +    (types_match_diagnostic_item(cx, expr, recv, sym::Vec) | 
|  | 37 | +        || types_match_diagnostic_item(cx, expr, recv, sym::VecDeque)) | 
|  | 38 | +        && matches!(args, [arg] if is_range_full(cx, arg, Some(recv_path))) | 
|  | 39 | +} | 
|  | 40 | + | 
|  | 41 | +/// Checks `std::string::String` | 
|  | 42 | +fn check_string(cx: &LateContext<'_>, args: &[Expr<'_>], expr: Ty<'_>, recv: Ty<'_>, recv_path: &Path<'_>) -> bool { | 
|  | 43 | +    is_type_lang_item(cx, expr, LangItem::String) | 
|  | 44 | +        && is_type_lang_item(cx, recv, LangItem::String) | 
|  | 45 | +        && matches!(args, [arg] if is_range_full(cx, arg, Some(recv_path))) | 
|  | 46 | +} | 
|  | 47 | + | 
|  | 48 | +/// Checks `std::collections::{HashSet, HashMap, BinaryHeap}`. | 
|  | 49 | +fn check_collections(cx: &LateContext<'_>, expr: Ty<'_>, recv: Ty<'_>) -> Option<&'static str> { | 
|  | 50 | +    types_match_diagnostic_item(cx, expr, recv, sym::HashSet) | 
|  | 51 | +        .then_some("HashSet") | 
|  | 52 | +        .or_else(|| types_match_diagnostic_item(cx, expr, recv, sym::HashMap).then_some("HashMap")) | 
|  | 53 | +        .or_else(|| types_match_diagnostic_item(cx, expr, recv, sym::BinaryHeap).then_some("BinaryHeap")) | 
|  | 54 | +} | 
|  | 55 | + | 
|  | 56 | +pub(super) fn check(cx: &LateContext<'_>, args: &[Expr<'_>], expr: &Expr<'_>, recv: &Expr<'_>) { | 
|  | 57 | +    let expr_ty = cx.typeck_results().expr_ty(expr); | 
|  | 58 | +    let recv_ty = cx.typeck_results().expr_ty(recv).peel_refs(); | 
|  | 59 | + | 
|  | 60 | +    if let ExprKind::Path(QPath::Resolved(_, recv_path)) = recv.kind | 
|  | 61 | +        && let Some(typename) = check_vec(cx, args, expr_ty, recv_ty, recv_path) | 
|  | 62 | +            .then_some("Vec") | 
|  | 63 | +            .or_else(|| check_string(cx, args, expr_ty, recv_ty, recv_path).then_some("String")) | 
|  | 64 | +            .or_else(|| check_collections(cx, expr_ty, recv_ty)) | 
|  | 65 | +    { | 
|  | 66 | +        let recv = snippet(cx, recv.span, "<expr>"); | 
|  | 67 | +        span_lint_and_sugg( | 
|  | 68 | +            cx, | 
|  | 69 | +            DRAIN_COLLECT, | 
|  | 70 | +            expr.span, | 
|  | 71 | +            &format!("you seem to be trying to move all elements into a new `{typename}`"), | 
|  | 72 | +            "consider using `mem::take`", | 
|  | 73 | +            format!("std::mem::take(&mut {recv})"), | 
|  | 74 | +            Applicability::MachineApplicable, | 
|  | 75 | +        ); | 
|  | 76 | +    } | 
|  | 77 | +} | 
0 commit comments