|
1 | 1 | use clippy_utils::diagnostics::span_lint_and_sugg;
|
2 | 2 | use clippy_utils::is_range_full;
|
3 |
| -use clippy_utils::ty::is_type_diagnostic_item; |
| 3 | +use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; |
4 | 4 | use rustc_errors::Applicability;
|
5 |
| -use rustc_hir::{Expr, ExprKind, QPath}; |
| 5 | +use rustc_hir as hir; |
| 6 | +use rustc_hir::{Expr, ExprKind, LangItem, QPath}; |
6 | 7 | use rustc_lint::LateContext;
|
7 | 8 | use rustc_span::symbol::sym;
|
8 | 9 | use rustc_span::Span;
|
9 | 10 |
|
10 | 11 | use super::CLEAR_WITH_DRAIN;
|
11 | 12 |
|
12 |
| -pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span, arg: &Expr<'_>) { |
13 |
| - let ty = cx.typeck_results().expr_ty(recv); |
14 |
| - if is_type_diagnostic_item(cx, ty, sym::Vec) |
15 |
| - && let ExprKind::Path(QPath::Resolved(None, container_path)) = recv.kind |
16 |
| - && is_range_full(cx, arg, Some(container_path)) |
| 13 | +// Add `String` here when it is added to diagnostic items |
| 14 | +const ACCEPTABLE_TYPES_WITH_ARG: [rustc_span::Symbol; 2] = [sym::Vec, sym::VecDeque]; |
| 15 | + |
| 16 | +const ACCEPTABLE_TYPES_WITHOUT_ARG: [rustc_span::Symbol; 3] = [sym::BinaryHeap, sym::HashMap, sym::HashSet]; |
| 17 | + |
| 18 | +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span, arg: Option<&Expr<'_>>) { |
| 19 | + if let Some(arg) = arg { |
| 20 | + if match_acceptable_type(cx, recv, &ACCEPTABLE_TYPES_WITH_ARG) |
| 21 | + && let ExprKind::Path(QPath::Resolved(None, container_path)) = recv.kind |
| 22 | + && is_range_full(cx, arg, Some(container_path)) |
| 23 | + { |
| 24 | + suggest(cx, expr, recv, span); |
| 25 | + } |
| 26 | + } else if match_acceptable_type(cx, recv, &ACCEPTABLE_TYPES_WITHOUT_ARG) { |
| 27 | + suggest(cx, expr, recv, span); |
| 28 | + } |
| 29 | +} |
| 30 | + |
| 31 | +fn match_acceptable_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>, types: &[rustc_span::Symbol]) -> bool { |
| 32 | + let expr_ty = cx.typeck_results().expr_ty(expr).peel_refs(); |
| 33 | + types.iter().any(|&ty| is_type_diagnostic_item(cx, expr_ty, ty)) |
| 34 | + // String type is a lang item but not a diagnostic item for now so we need a separate check |
| 35 | + || is_type_lang_item(cx, expr_ty, LangItem::String) |
| 36 | +} |
| 37 | + |
| 38 | +fn suggest(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span) { |
| 39 | + if let Some(adt) = cx.typeck_results().expr_ty(recv).ty_adt_def() |
| 40 | + // Use `opt_item_name` while `String` is not a diagnostic item |
| 41 | + && let Some(ty_name) = cx.tcx.opt_item_name(adt.did()) |
17 | 42 | {
|
18 | 43 | span_lint_and_sugg(
|
19 | 44 | cx,
|
20 | 45 | CLEAR_WITH_DRAIN,
|
21 | 46 | span.with_hi(expr.span.hi()),
|
22 |
| - "`drain` used to clear a `Vec`", |
| 47 | + &format!("`drain` used to clear a `{ty_name}`"), |
23 | 48 | "try",
|
24 | 49 | "clear()".to_string(),
|
25 | 50 | Applicability::MachineApplicable,
|
|
0 commit comments