]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/types/redundant_allocation.rs
8638197a5842f68317f1137ea3330899cfead3eb
[rust.git] / clippy_lints / src / types / redundant_allocation.rs
1 use clippy_utils::diagnostics::span_lint_and_then;
2 use clippy_utils::source::{snippet, snippet_with_applicability};
3 use clippy_utils::{is_ty_param_diagnostic_item, is_ty_param_lang_item, qpath_generic_tys};
4 use rustc_errors::Applicability;
5 use rustc_hir::{self as hir, def_id::DefId, LangItem, QPath, TyKind};
6 use rustc_lint::LateContext;
7 use rustc_span::symbol::sym;
8
9 use super::{utils, REDUNDANT_ALLOCATION};
10
11 pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool {
12     let outer_sym = if Some(def_id) == cx.tcx.lang_items().owned_box() {
13         "Box"
14     } else if cx.tcx.is_diagnostic_item(sym::Rc, def_id) {
15         "Rc"
16     } else if cx.tcx.is_diagnostic_item(sym::Arc, def_id) {
17         "Arc"
18     } else {
19         return false;
20     };
21
22     if let Some(span) = utils::match_borrows_parameter(cx, qpath) {
23         let mut applicability = Applicability::MaybeIncorrect;
24         let generic_snippet = snippet_with_applicability(cx, span, "..", &mut applicability);
25         span_lint_and_then(
26             cx,
27             REDUNDANT_ALLOCATION,
28             hir_ty.span,
29             &format!("usage of `{}<{}>`", outer_sym, generic_snippet),
30             |diag| {
31                 diag.span_suggestion(hir_ty.span, "try", format!("{}", generic_snippet), applicability);
32                 diag.note(&format!(
33                     "`{generic}` is already a pointer, `{outer}<{generic}>` allocates a pointer on the heap",
34                     outer = outer_sym,
35                     generic = generic_snippet
36                 ));
37             },
38         );
39         return true;
40     }
41
42     let (inner_sym, ty) = if let Some(ty) = is_ty_param_lang_item(cx, qpath, LangItem::OwnedBox) {
43         ("Box", ty)
44     } else if let Some(ty) = is_ty_param_diagnostic_item(cx, qpath, sym::Rc) {
45         ("Rc", ty)
46     } else if let Some(ty) = is_ty_param_diagnostic_item(cx, qpath, sym::Arc) {
47         ("Arc", ty)
48     } else {
49         return false;
50     };
51
52     let inner_qpath = match &ty.kind {
53         TyKind::Path(inner_qpath) => inner_qpath,
54         _ => return false,
55     };
56     let inner_span = match qpath_generic_tys(inner_qpath).next() {
57         Some(ty) => {
58             // Box<Box<dyn T>> is smaller than Box<dyn T> because of wide pointers
59             if matches!(ty.kind, TyKind::TraitObject(..)) {
60                 return false;
61             }
62             ty.span
63         },
64         None => return false,
65     };
66     if inner_sym == outer_sym {
67         let mut applicability = Applicability::MaybeIncorrect;
68         let generic_snippet = snippet_with_applicability(cx, inner_span, "..", &mut applicability);
69         span_lint_and_then(
70             cx,
71             REDUNDANT_ALLOCATION,
72             hir_ty.span,
73             &format!("usage of `{}<{}<{}>>`", outer_sym, inner_sym, generic_snippet),
74             |diag| {
75                 diag.span_suggestion(
76                     hir_ty.span,
77                     "try",
78                     format!("{}<{}>", outer_sym, generic_snippet),
79                     applicability,
80                 );
81                 diag.note(&format!(
82                     "`{inner}<{generic}>` is already on the heap, `{outer}<{inner}<{generic}>>` makes an extra allocation",
83                     outer = outer_sym,
84                     inner = inner_sym,
85                     generic = generic_snippet
86                 ));
87             },
88         );
89     } else {
90         let generic_snippet = snippet(cx, inner_span, "..");
91         span_lint_and_then(
92             cx,
93             REDUNDANT_ALLOCATION,
94             hir_ty.span,
95             &format!("usage of `{}<{}<{}>>`", outer_sym, inner_sym, generic_snippet),
96             |diag| {
97                 diag.note(&format!(
98                     "`{inner}<{generic}>` is already on the heap, `{outer}<{inner}<{generic}>>` makes an extra allocation",
99                     outer = outer_sym,
100                     inner = inner_sym,
101                     generic = generic_snippet
102                 ));
103                 diag.help(&format!(
104                     "consider using just `{outer}<{generic}>` or `{inner}<{generic}>`",
105                     outer = outer_sym,
106                     inner = inner_sym,
107                     generic = generic_snippet
108                 ));
109             },
110         );
111     }
112     true
113 }